123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525 |
- /**
- * 级联选择器模块
- * 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 = '<div class="ew-cascader-group">';
- htmlStr += ' <div class="ew-cascader-input-group">';
- htmlStr += ' <input class="layui-input ew-cascader-input" readonly/>';
- htmlStr += ' <input class="layui-input ew-cascader-input-search"/>';
- htmlStr += ' <i class="layui-icon layui-icon-triangle-d ew-icon-arrow"></i>';
- htmlStr += ' <i class="layui-icon layui-icon-loading-1 layui-anim layui-anim-rotate layui-anim-loop ew-icon-loading"></i>';
- htmlStr += ' <i class="layui-icon layui-icon-close-fill ew-icon-clear"></i>';
- htmlStr += ' </div>';
- htmlStr += ' <div class="ew-cascader-dropdown layui-anim layui-anim-upbit"></div>';
- htmlStr += ' <div class="ew-cascader-search-list"></div>';
- htmlStr += ' </div>';
- $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'), '<span class="search-keyword">' + value + '</span>');
- 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 = '<div class="ew-cascader-dropdown-list" ' + style + '>';
- 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 += ' <div class="ew-cascader-dropdown-list-item' + className + '" data-index="' + index + '" data-value="' + item.value + '">' + item.label + '<i class="layui-icon layui-icon-right ew-icon-right"></i><i class="layui-icon layui-icon-loading-1 layui-anim layui-anim-rotate layui-anim-loop ew-icon-loading"></i></div>';
- }
- htmlStr += ' </div>';
- $dropdown.append(htmlStr);
- cascader.checkWidthPosition($dropdown); // 检查是否溢出屏幕
- };
- /** 渲染搜索列表 */
- var renderSearchList = function ($search, dataList, notFoundText) {
- var htmlStr = '';
- if (dataList.length == 0) {
- htmlStr = '<div class="ew-cascader-search-list-empty">' + notFoundText + '</div>';
- } else {
- for (var i = 0; i < dataList.length; i++) {
- var item = dataList[i];
- var className = item.disabled ? ' ew-cascader-disabled' : ''; // 是否禁用
- htmlStr += '<div class="ew-cascader-search-list-item' + className + '" data-value="' + item.value + '">' + item.label + '</div>';
- }
- }
- $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);
- });
|