/** * 级联选择器模块 * date:2019-07-27 License By http://easyweb.vip */ layui.define(function (exports) { var $ = layui.jquery; layui.link(layui.cache.base + 'cascader/cascader.css'); var onVisibleChangeList = []; // 所有展开和关闭回调 var cascader = { /* 初始化 */ render: function (param) { // 默认参数 var defaultOptions = { renderFormat: function (labels, values) { return labels.join(' / '); }, clearable: true, clearAllActive: false, disabled: false, trigger: 'click', changeOnSelect: false, filterable: false, notFoundText: '没有匹配数据' }; param = $.extend(defaultOptions, param); var elem = param.elem; // 目标元素 var mDataList = param.data; // 数据 var renderFormat = param.renderFormat; // 选择后用于展示的函数 var clearable = param.clearable; // 是否支持清除 var clearAllActive = param.clearAllActive; // 清除所有选中 var disabled = param.disabled; // 是否禁用 var trigger = param.trigger; // 次级菜单触发方式 var changeOnSelect = param.changeOnSelect; // 是否点击每项选项值都改变 var reqData = param.reqData; // 自定义获取数据的方法 var filterable = param.filterable; // 是否开启搜索 var notFoundText = param.notFoundText; // 搜索列表为空时显示的内容 var reqSearch = param.reqSearch; // 自定义搜索的方法 var onChange = param.onChange; // 数据选择完成的回调 var onVisibleChange = param.onVisibleChange; // 展开和关闭弹窗时触发 var itemHeight = param.itemHeight; // 下拉列表每一项高度 var isFirst = true; // 如果渲染过重新渲染 var $elem = $(elem); if ($elem.next().hasClass('ew-cascader-group')) { $elem.next().remove(); for (var i = 0; i < onVisibleChangeList.length; i++) { if (elem == onVisibleChangeList[i].elem) { onVisibleChangeList.splice(i, 1); break; } } } onVisibleChangeList.push({elem: elem, onVisibleChange: onVisibleChange}); $elem.addClass('ew-cascader-hide'); var htmlStr = '
'; htmlStr += '
'; htmlStr += ' '; htmlStr += ' '; htmlStr += ' '; htmlStr += ' '; htmlStr += ' '; htmlStr += '
'; htmlStr += '
'; htmlStr += '
'; htmlStr += '
'; $elem.after(htmlStr); var $cascader = $elem.next(); var $inputGroup = $cascader.children('.ew-cascader-input-group'); var $input = $inputGroup.children('.ew-cascader-input'); var $inputSearch = $inputGroup.children('.ew-cascader-input-search'); var $dropdown = $cascader.children('.ew-cascader-dropdown'); var $search = $cascader.children('.ew-cascader-search-list'); $input.attr('placeholder', $elem.attr('placeholder')); disabled && $input.addClass('layui-disabled'); // 构建渲染后的实例 var _instance = { data: mDataList, /* 展开 */ open: function () { if ($cascader.hasClass('ew-cascader-open')) { return; } cascader.hideAll(); $cascader.addClass('ew-cascader-open'); cascader.checkWidthPosition($dropdown); // 溢出屏幕判断 cascader.checkHeightPosition($dropdown); // 溢出屏幕判断 onVisibleChange && onVisibleChange(true); // 展开回调 if (filterable) { // 如果开启搜索功能让输入框获取焦点 $inputGroup.addClass('show-search'); $inputSearch.focus(); } }, /* 关闭 */ hide: function () { if (!$cascader.hasClass('ew-cascader-open')) { return; } $cascader.removeClass('ew-cascader-open'); $cascader.removeClass('dropdown-show-top'); $cascader.removeClass('dropdown-show-left'); cascader.hideAllSearch(); onVisibleChange && onVisibleChange(false); // 关闭回调 }, /* 移除加载中的状态*/ removeLoading: function () { $cascader.removeClass('show-loading'); $dropdown.find('.ew-cascader-dropdown-list-item').removeClass('show-loading'); }, /* 设置禁用状态 */ setDisabled: function (dis) { disabled = dis; if (dis) { $input.addClass('layui-disabled'); _instance.hide(); } else { $input.removeClass('layui-disabled'); } }, /* 获取值*/ getValue: function () { return $elem.val(); }, /* 获取label */ getLabel: function () { return $input.val(); }, /* 设置值*/ setValue: function (value) { if (value == undefined || value == null || !value.toString()) { $input.val(''); $elem.val(''); if (clearAllActive || changeOnSelect) { // 清除所有 $dropdown.children('.ew-cascader-dropdown-list').not(':first').remove(); $dropdown.find('.ew-cascader-dropdown-list-item').removeClass('active'); cascader.checkWidthPosition($dropdown); // 溢出屏幕判断 } else { // 仅清除最后一项 $dropdown.find('.ew-cascader-dropdown-list-item.is-last').removeClass('active'); } $inputGroup.removeClass('show-clear'); return; } value = value.toString().split(','); var labels = []; // 通过递归控制异步加载回显默认值对应label时的请求顺序 function doReqData(tValues, data, i, values, callback) { if (!tValues && data) { tValues = []; setData(data); } else if (tValues && data && data.children) { setData(data.children); } else { // 数据不存在时才去请求数据 $cascader.addClass('show-loading'); reqData(tValues, function (dataList) { if (tValues) { data.children = dataList; } else { mDataList = dataList; tValues = []; } setData(dataList); }, data); } function setData(dataList) { for (var j = 0; j < dataList.length; j++) { if (dataList[j].value == values[i]) { labels[i] = dataList[j].label; tValues[i] = dataList[j].value; if (i < values.length - 1) { doReqData(tValues, dataList[j], i + 1, values, callback); } else { callback(); } break; } } } } doReqData(undefined, mDataList, 0, value, function () { $cascader.removeClass('show-loading'); $input.val(renderFormat(labels, value)); $elem.val(value.join(',')); }); } }; // 回显初始值 _instance.setValue($elem.val()); // 点击展开/关闭下拉列表 $inputGroup.off('click').on('click', function (e) { // 判断是否是禁用状态 if ($input.hasClass('layui-disabled')) { return; } // 判断是否是加载中状态 if ($cascader.hasClass('show-loading')) { return; } // 关闭 if ($cascader.hasClass('ew-cascader-open')) { if (!filterable) { // 是否开启搜索功能 _instance.hide(); } return; } // 展开 if (isFirst) { // 第一次展开渲染第一列数据 if (mDataList) { isFirst = false; renderList($dropdown, mDataList, undefined, itemHeight); initLabel(); _instance.open(); } else { // 异步方式 $cascader.addClass('show-loading'); reqData(undefined, function (dataList) { isFirst = false; mDataList = dataList; renderList($dropdown, dataList, undefined, itemHeight); $cascader.removeClass('show-loading'); _instance.open(); }, undefined); } } else { initLabel(); _instance.open(); } // 回显上次选中项 function initLabel() { var value = $elem.val().toString(); if (value) { value = value.split(','); for (var i = 0; i < value.length; i++) { var $item = $dropdown.children('.ew-cascader-dropdown-list').eq(i).children('.ew-cascader-dropdown-list-item[data-value="' + value[i] + '"]'); if (i == value.length - 1) { $item.addClass('active'); } else { $item.trigger('click'); } } } else { _instance.setValue(); } } }); $inputGroup.children('.ew-icon-arrow').off('click').on('click', function (e) { if ($cascader.hasClass('ew-cascader-open')) { _instance.hide(); e.stopPropagation(); } }); // 点击渲染次级列表 $dropdown.off('click').on('click', '.ew-cascader-dropdown-list-item', function () { var $this = $(this); // 防止重复点击 if ($this.hasClass('active')) { if ($this.hasClass('is-last')) { _instance.hide(); } return; } // 判断是否是禁用状态 if ($this.hasClass('ew-cascader-disabled')) { return; } // 判断是否是加载中状态 if ($this.parent().parent().find('.ew-cascader-dropdown-list-item').hasClass('show-loading')) { return; } var index = $this.data('index').toString(); var indexList = index.split('-'); var data = mDataList[parseInt(indexList[0])], values = [data.value], labels = [data.label]; for (var i = 1; i < indexList.length; i++) { data = data.children[parseInt(indexList[i])]; values[i] = data.value; labels[i] = data.label; } if (data.haveChildren) { // 非最后一项 if (data.children) { // 数据方式或已经异步加载直接渲染 $this.parent().nextAll().remove(); cascader.checkWidthPosition($dropdown); // 检查是否溢出屏幕 activeThis(); renderList($dropdown, data.children, index, itemHeight); } else { // 异步方式先请求数据再渲染 $this.addClass('show-loading'); reqData(values, function (dataList) { data.children = dataList; $this.parent().nextAll().remove(); cascader.checkWidthPosition($dropdown); // 检查是否溢出屏幕 activeThis(); renderList($dropdown, dataList, index, itemHeight); $this.removeClass('show-loading'); }, data); } // 点击非最后一项也触发选中 if (changeOnSelect) { activeThis(); doChange(); } } else { // 最后一项 $this.parent().nextAll().remove(); activeThis(); doChange(); _instance.hide(); // 选中后关闭 } /* 选中当前 */ function activeThis() { $this.parent().children('.ew-cascader-dropdown-list-item').removeClass('active'); $this.addClass('active'); } /* 触发选中回调 */ function doChange() { $input.val(renderFormat(labels, values)); // 赋值label $elem.val(values.join(',')); // 赋值value $elem.removeClass('layui-form-danger'); // 移除表单验证 onChange && onChange(values, data); // 选中回调 } }); // hover方式触发 if (trigger == 'hover') { $dropdown.off('mouseenter').on('mouseenter', '.ew-cascader-dropdown-list-item', function () { if (!$(this).hasClass('is-last')) { $(this).trigger('click'); } }); } // 开启清除功能 if (clearable) { $inputGroup.off('mouseenter').on('mouseenter', function () { if ($elem.val().toString()) { $(this).addClass('show-clear'); } }); $inputGroup.off('mouseleave').on('mouseleave', function () { $(this).removeClass('show-clear'); }); // 点击清除 $inputGroup.children('.ew-icon-clear').off('click').on('click', function (e) { e.stopPropagation(); _instance.setValue(); }); } // 开启搜索功能 if (filterable) { $inputSearch.off('input').on('input', function () { var value = $(this).val(); if (!value) { $cascader.removeClass('show-search-list'); $inputGroup.removeClass('have-value'); return; } $inputGroup.addClass('have-value'); if (reqSearch) { // 异步搜索 reqSearch(value, function (rsList) { // 渲染搜索结果 renderSearchList($search, rsList, notFoundText); $cascader.addClass('show-search-list'); }, mDataList); } else { // 前端搜索 var allList = [], rsList = []; // 把树形list转一维list function toAllList(arr, label, value, disabled) { for (var i = 0; i < arr.length; i++) { var item = arr[i]; item.__label = label ? label + ' / ' + item.label : item.label; item.__value = value ? value + ',' + item.value : item.value; item.__disabled = item.disabled ? item.disabled : disabled; if (item.children && item.children.length) { toAllList(item.children, item.__label, item.__value, item.__disabled); delete item.__label; delete item.__value; } else { allList.push({ label: item.__label, value: item.__value, disabled: item.__disabled }); } } } toAllList(mDataList); // 过滤数据 for (var i = 0; i < allList.length; i++) { var item = allList[i]; if (item.label.indexOf(value) > -1) { item.label = item.label.replace(new RegExp(value, 'g'), '' + value + ''); rsList.push(item); } } // 渲染搜索结果 renderSearchList($search, rsList, notFoundText); $cascader.addClass('show-search-list'); } }); $search.off('click').on('click', '.ew-cascader-search-list-item', function (e) { e.stopPropagation(); if ($(this).hasClass('ew-cascader-disabled')) { // 是否禁用 return; } _instance.hide(); _instance.setValue($(this).data('value')); }); } return _instance; }, /** 关闭所有 */ hideAll: function () { cascader.hideAllSearch(); for (var i = 0; i < onVisibleChangeList.length; i++) { var elem = onVisibleChangeList[i].elem; var onVisibleChange = onVisibleChangeList[i].onVisibleChange; var $cascader = $(elem).next(); if ($cascader.hasClass('ew-cascader-open')) { $cascader.removeClass('ew-cascader-open'); $cascader.removeClass('dropdown-show-top'); $cascader.removeClass('dropdown-show-left'); onVisibleChange && onVisibleChange(false); } } }, /** 关闭所有搜索面板 */ hideAllSearch: function () { $('.ew-cascader-input-group').removeClass('show-search'); $('.ew-cascader-group').removeClass('show-search-list'); $('.ew-cascader-input-group').removeClass('have-value'); $('.ew-cascader-input-search').val(''); }, /* 获取浏览器高度 */ getPageHeight: function () { return document.documentElement.clientHeight || document.body.clientHeight; }, /* 获取浏览器宽度 */ getPageWidth: function () { return document.documentElement.clientWidth || document.body.clientWidth; }, /* 检查宽度是否溢出屏幕 */ checkWidthPosition: function ($dropdown) { if ($dropdown.offset().left + $dropdown.outerWidth() > cascader.getPageWidth()) { $dropdown.parent().addClass('dropdown-show-left'); } else { $dropdown.parent().removeClass('dropdown-show-left'); } }, /* 检查高度是否溢出屏幕 */ checkHeightPosition: function ($dropdown) { if ($dropdown.offset().top + $dropdown.outerHeight() > cascader.getPageHeight()) { $dropdown.parent().addClass('dropdown-show-top'); if ($dropdown.offset().top < 0) { $dropdown.parent().removeClass('dropdown-show-top'); } } } }; /** 渲染列表 */ var renderList = function ($dropdown, dataList, pIndex, itemHeight) { var style = itemHeight ? ' style="height:' + itemHeight + ';"' : ''; var htmlStr = '
'; for (var i = 0; i < dataList.length; i++) { var item = dataList[i]; var index = pIndex == undefined ? i : (pIndex + '-' + i); if (item.haveChildren == undefined) { item.haveChildren = item.children ? true : false; } var className = item.haveChildren ? '' : ' is-last'; // 是否是叶子节点 item.disabled && (className += ' ew-cascader-disabled'); // 是否禁用 htmlStr += '
' + item.label + '
'; } htmlStr += '
'; $dropdown.append(htmlStr); cascader.checkWidthPosition($dropdown); // 检查是否溢出屏幕 }; /** 渲染搜索列表 */ var renderSearchList = function ($search, dataList, notFoundText) { var htmlStr = ''; if (dataList.length == 0) { htmlStr = '
' + notFoundText + '
'; } else { for (var i = 0; i < dataList.length; i++) { var item = dataList[i]; var className = item.disabled ? ' ew-cascader-disabled' : ''; // 是否禁用 htmlStr += '
' + item.label + '
'; } } $search.html(htmlStr); }; // 点击空白区域关闭下拉列表 $(document).off('click.cascader').on('click.cascader', function (e) { try { var classNames = e.target.className.split(' '); var cascaders = ['ew-cascader-group', 'ew-cascader-input', 'ew-icon-arrow', 'ew-cascader-dropdown', 'ew-cascader-dropdown-list', 'ew-cascader-dropdown-list-item', 'ew-icon-right', 'ew-cascader-input-search', 'ew-cascader-search-list', 'ew-cascader-search-list-item']; for (var i in classNames) { for (var j in cascaders) { if (classNames[i] == cascaders[j]) { return; } } } } catch (e) { } cascader.hideAll(); }); exports('cascader', cascader); });