/** * 仿element-ui,级联选择器 * 已实现单选,多选,无关联选择 * 其他功能:组件禁用、节点禁用、自定义属性、自定义空面板,自定义无选择时的提示、多选标签折叠、回显等操作。 * author: yixiaco */ layui.define(["jquery"], function (exports) { var $ = layui.jquery; /** * 级联各项节点对象 * @param data 原始对象信息 * @param cascader 级联对象 * @param level 层级 * @param parentNode 父节点对象 * @constructor */ function Node(data, cascader, level, parentNode) { this.data = data; this.cascader = cascader; this.config = cascader.config; this.props = cascader.props; this.level = level; this.parentNode = parentNode; this.icons = cascader.icons; this._checked = 0; } Node.prototype = { constructor: Node, /** 该节点是否被选中 0:未选中,1:选中,2:不定*/ get checked() { return this._checked; }, /** * 复选框的值,此处不会自动同步父节点的状态,需要自行处理 * @param checked 0:未选中,1:选中,2:不定 */ set checked(checked) { if (this._checked === checked) { return; } this._checked = checked; var value = this.value; var cascader = this.cascader; var checkStrictly = this.props.checkStrictly; var leaf = this.leaf; var index = cascader.data.checkedValue.indexOf(value); var length = cascader.data.checkedValue.length; var $checkbox; if (checked) { if (checkStrictly || leaf) { if (length === 0) { cascader._setClear(); } cascader.data.checkedValue.push(value); cascader.data.checkedNodePaths.push(this); } if (this.$checked) { $checkbox = this.$checked.find('.el-checkbox__input'); if (checked === 1) { $checkbox.removeClass('is-indeterminate'); $checkbox.addClass('is-checked'); } else if (checked === 2) { $checkbox.removeClass('is-checked'); $checkbox.addClass('is-indeterminate'); } } } else { if (checkStrictly || leaf) { cascader.data.checkedValue.splice(index, 1); cascader.data.checkedNodePaths.splice(index, 1); } if (this.$checked) { $checkbox = this.$checked.find('.el-checkbox__input'); $checkbox.removeClass('is-checked'); $checkbox.removeClass('is-indeterminate'); } } if (length !== cascader.data.checkedValue.length) { cascader.change(cascader.data.checkedValue, cascader.data.checkedNodePaths); } }, /** 子节点 */ childrenNode: undefined, /** 当前节点的显示文本 */ get label() { return this.data[this.props.label]; }, /** 当前节点的值 */ get value() { return this.data[this.props.value]; }, /** 是否禁用 */ get disabled() { return this.data[this.props.disabled]; }, /** 子节点数据 */ get children() { return this.data[this.props.children]; }, /** 叶子节点 */ get leaf() { var leaf = this.data[this.props.leaf]; if (typeof leaf === 'boolean') { return leaf; } // 如果children不为空,则判断是否是子节点 if (this.children) { return this.children.length <= 0; } return true; }, /** 当前单选值 */ get currentValue() { return this.cascader.data.value; }, /** 当前复选框值 */ get currentCheckedValue() { return this.cascader.data.checkedValue; }, /** 路径 */ get path() { var parentNode = this.parentNode; if (parentNode) { return [].concat(parentNode.path, [this]); } else { return [this]; } }, /** 是否正在搜索中 */ get isFiltering() { return this.cascader.isFiltering; }, /** 输入框的tag标签 */ get $tag() { var cascader = this.cascader; var checkStrictly = this.props.checkStrictly; var showAllLevels = this.config.showAllLevels; var label = this.getPathLabel(showAllLevels); var $tag = cascader.get$tag(label, true); var self = this; $tag.find('i').click(function (event) { event.stopPropagation(); self.checked = 0; if (!checkStrictly) { // 向上传递 self._syncTransferParent(); } else { // 向上多选非关联传递 self._syncTransferCheckStrictlyParent(); } cascader.removeTag(self.value, self); }); return $tag; }, /** * 完整路径的标签 * @param showAllLevels * @returns {string} */ getPathLabel: function (showAllLevels) { showAllLevels = showAllLevels || true; var path = this.path; var separator = this.config.separator; var label; if (showAllLevels) { label = path.map(function (node) { return node.label; }).join(separator); } else { label = path[path.length - 1].label; } return label; }, /** * 初始化 */ init: function () { var multiple = this.props.multiple; var checkStrictly = this.props.checkStrictly; var fromIcon = this.icons.from; var rightIcon = this.icons.right; var icon = ''; var label = this.label; if (!this.leaf) { icon = rightIcon; } this.$li = $(''); // 节点渲染 if (!multiple && !checkStrictly) { this._renderRadio(); } else if (!multiple && checkStrictly) { this._renderRadioCheckStrictly(); } else if (multiple && !checkStrictly) { this._renderMultiple(); } else if (multiple && checkStrictly) { this._renderMultipleCheckStrictly(); } }, /** * 初始化可搜索li */ initSuggestionLi: function () { var label = this.getPathLabel(); this.$suggestionLi = $('
  • ' + label + '
  • '); // 节点渲染 this._renderFiltering(); }, /** * 绑定到菜单中 * @param $list li节点 */ bind: function ($list) { this.init(); $list.append(this.$li); }, /** * 绑定可搜索到列表中 * @param $list */ bindSuggestion: function ($list) { this.initSuggestionLi(); $list.append(this.$suggestionLi); }, /** * 可搜索渲染 * @private */ _renderFiltering: function () { var $li = this.$suggestionLi; var value = this.value; var fromIcon = this.icons.from; var okIcon = this.icons.ok; var self = this; var cascader = this.cascader; var multiple = this.props.multiple; var checkStrictly = this.props.checkStrictly; var parentNode = this.parentNode; // 是否禁用 if (this.disabled) { $li.addClass('is-disabled'); return; } var icon = ''; $li.click(function (event) { event.stopPropagation(); if (multiple) { if (self.currentCheckedValue.indexOf(value) === -1) { $li.addClass('is-checked'); $li.append(icon); } else { $li.removeClass('is-checked'); $li.find('.el-icon-check').remove(); } self.checked = self.checked === 0 ? 1 : 0; if (checkStrictly) { self._syncTransferCheckStrictlyParent(); } else { // 向上传递 self._syncTransferParent(); } } else { if (self.currentValue !== value) { if (checkStrictly) { self._syncRadioCheckStrictly(); } else { self._syncRadio(); } self.currentValue = value; parentNode.$li && parentNode.$li.click(); $li.addClass('is-checked'); $li.append(icon); } // 关闭面板 cascader.blur(event); } }); if (multiple && self.currentCheckedValue.indexOf(value) !== -1 || !multiple && self.currentValue === value) { $li.addClass('is-checked'); $li.append(icon) } }, /** * 单选&&关联 * @private */ _renderRadio: function () { var $li = this.$li; var value = this.value; var fromIcon = this.icons.from; var okIcon = this.icons.ok; var level = this.level; var childrenNode = this.childrenNode; var leaf = this.leaf; var self = this; var cascader = this.cascader; var activeNode = this.cascader.data.activeNode; // 是否禁用 if (this.disabled) { $li.addClass('is-disabled'); return; } // 触发下一个节点 this._liClick(function (event) { event.stopPropagation(); self._syncRadio(); if (leaf) { self.currentValue = value; // 关闭面板 cascader.blur(event); } // 添加下级菜单 cascader._appendMenu(childrenNode, level + 1, self); }); if (self.currentValue && activeNode.path.some(function (node) { return node.value === value; })) { if (self.currentValue === value) { $li.prepend(''); } $li.addClass('is-active'); } }, /** * 同步单选关联的状态 * @private */ _syncRadio: function () { var $li = this.$li; var leaf = this.leaf; var fromIcon = this.icons.from; var okIcon = this.icons.ok; if (!$li) { return; } $li.siblings().removeClass('is-active'); $li.siblings().find('.' + okIcon).remove(); $li.addClass('is-active'); if (leaf) { if (this.currentValue !== this.value) { $li.prepend(''); } } }, /** * 单选&&非关联 * @private */ _renderRadioCheckStrictly: function () { var $li = this.$li; var value = this.value; var level = this.level; var childrenNode = this.childrenNode; var leaf = this.leaf; var self = this; var cascader = this.cascader; var activeNode = cascader.data.activeNode; $li.addClass('is-selectable'); // 任意一级单选 var $radio = $(''); this.$radio = $radio; $li.prepend($radio); // 触发下一个节点 this._liClick(function (event) { event.stopPropagation(); if (!leaf) { $li.siblings().removeClass('in-active-path'); $li.addClass('in-active-path'); } // 添加下级菜单 cascader._appendMenu(childrenNode, level + 1, self); }); if (this.disabled) { $radio.addClass('is-disabled'); $radio.find('.el-radio__input').addClass('is-disabled'); return; } // 选中事件 $radio.click(function (event) { event.preventDefault(); self._syncRadioCheckStrictly(); self.currentValue = value; // 重新加载一下下级菜单 cascader._appendMenu(childrenNode, level + 1, self); }); if (self.currentValue && activeNode.path.some(function (node) { return node.value === value; })) { if (self.currentValue === value) { $radio.find('.el-radio__input').addClass('is-checked'); } $li.addClass('is-active'); $li.addClass('in-checked-path'); } }, /** * 同步单选非关联的状态 * @private */ _syncRadioCheckStrictly: function () { var $radio = this.$radio; this.transferParent(function (node) { var $li = node.$li; if (!$li) { return; } $li.siblings().find('.el-radio__input').removeClass('is-checked'); $li.find('.el-radio__input').removeClass('is-checked'); $li.siblings().removeClass('in-active-path'); $li.siblings().removeClass('is-active'); $li.siblings().removeClass('in-checked-path'); $li.addClass('in-active-path'); $li.addClass('is-active'); $li.addClass('in-checked-path'); }, true); $radio && $radio.find('.el-radio__input').addClass('is-checked'); }, /** * 向上传递 * @param callback 执行方法,如果返回false,则中断执行 * @param advance 是否先执行一次 * @param self 自身 */ transferParent: function (callback, advance, self) { if (!self) { self = this; } if (this !== self || advance) { var goOn = callback && callback(this); if (goOn === false) { return; } } this.parentNode && this.parentNode.transferParent(callback, advance, self); }, /** * 向下传递 * @param callback 执行的方法,如果返回false,则中断执行 * @param advance 是否先执行一次 * @param self 自身 */ transferChildren: function (callback, advance, self) { if (!self) { self = this; } if (this !== self || advance) { var goOn = callback && callback(this); if (goOn === false) { return; } } var children = this.getChildren(); if (children && children.length > 0) { for (var index in children) { children[index].transferChildren(callback, advance, self); } } }, /** * 设置级联值 * @param value */ set currentValue(value) { var cascader = this.cascader; if (cascader.data.value !== value) { cascader.data.value = value; cascader.data.activeNode = this; // 填充路径 cascader.change(cascader.data.value, cascader.data.activeNode); cascader._setClear(); } }, /** * 多选&&关联 * @private */ _renderMultiple: function () { var $li = this.$li; var level = this.level; var childrenNode = this.childrenNode; var leaf = this.leaf; var self = this; var cascader = this.cascader; var checked = this.checked; $li.addClass('el-cascader-node'); // 多选框 var $checked = $(''); this.$checked = $checked; $li.prepend($checked); // 渲染 if (checked === 1) { this.$checked.find('.el-checkbox__input').addClass('is-checked'); } else if (checked === 2) { this.$checked.find('.el-checkbox__input').addClass('is-indeterminate'); } if (this.disabled) { $li.addClass('is-disabled'); $checked.addClass('is-disabled'); $checked.find('.el-checkbox__input').addClass('is-disabled'); return; } // 触发下一个节点 this._liClick(function (event) { event.stopPropagation(); if (!leaf) { $li.siblings().removeClass('in-active-path'); $li.addClass('in-active-path'); } // 添加下级菜单 cascader._appendMenu(childrenNode, level + 1, self); }); // 选中事件 $checked.click(function (event) { event.preventDefault(); if (leaf) { self.checked = self.checked === 0 ? 1 : 0; } else { self.checked = self.checkedValue(self, self.checked === 0); // 向下传递 self.transferChildren(function (node) { if (node.disabled) { return false; } node.checked = self.checkedValue(node, self.checked !== 0); }); } // 向上传递 self._syncTransferParent(); }); }, /** * 检查子节点,给父节点赋值选中样式 * @param node * @param checked * @returns {number|number} */ checkedValue: function (node, checked) { if (node.leaf) { return checked ? 1 : 0; } else { var isDisabled = false; var isChecked = checked; // 向下传递 node.transferChildren(function (child) { if (child.disabled) { isDisabled = true; return false; } if (child.checked === 0) { isChecked = true; } }); if (isDisabled && isChecked) { // 有禁用,有未选->不定 return 2; } else if (isChecked) { // 节点不定或者未选中->选中 return 1; } else { return 0; } } }, /** * 多选&&非关联 * @private */ _renderMultipleCheckStrictly: function () { var $li = this.$li; var level = this.level; var childrenNode = this.childrenNode; var leaf = this.leaf; var self = this; var cascader = this.cascader; var checked = this.checked; var checkedNodePaths = cascader.data.checkedNodePaths; var selfValue = this.value; $li.addClass('el-cascader-node is-selectable'); // 多选框 var $checked = $(''); this.$checked = $checked; $li.prepend($checked); // 渲染 var exist = checkedNodePaths.some(function (node) { return node.path.some(function (node) { return node.value === selfValue; }) }); if (exist) { $li.addClass('in-checked-path'); if (checked === 1) { this.$checked.find('.el-checkbox__input').addClass('is-checked'); } } // 触发下一个节点 this._liClick(function (event) { event.stopPropagation(); if (!leaf) { $li.siblings().removeClass('in-active-path'); $li.addClass('in-active-path'); } // 添加下级菜单 cascader._appendMenu(childrenNode, level + 1, self); }); if (this.disabled) { $checked.addClass('is-disabled'); $checked.find('.el-checkbox__input').addClass('is-disabled'); return; } // 选中事件 $checked.click(function (event) { event.preventDefault(); self.checked = self.checked === 0 ? 1 : 0; self._syncTransferCheckStrictlyParent(); }); }, /** * 关联的复选框父节点同步状态 * @private */ _syncTransferParent: function () { var self = this; // 向上传递 self.transferParent(function (node) { var checked; var map = node.childrenNode.map(function (child) { return child.checked; }); if (map.indexOf(2) !== -1 || (map.indexOf(0) !== -1 && map.indexOf(1) !== -1)) { // 有不定就是不定,有选中和未选中也是不定 checked = 2; } else if (map.indexOf(1) !== -1) { // 有选中,并且没有了未选中和不定 checked = 1; } else { // 只剩下未选中 checked = 0; } node.checked = checked; }); }, /** * 多选无关联同步父级节点样式 * @private */ _syncTransferCheckStrictlyParent: function () { var self = this; var cascader = this.cascader; var checkedNodePaths = cascader.data.checkedNodePaths; // 向上传递 self.transferParent(function (transferNode) { var $li = transferNode.$li; if ($li) { // 渲染 var exist = checkedNodePaths.some(function (node) { return node.path.some(function (node) { return node.value === transferNode.value; }) }); if (exist) { $li.addClass('in-checked-path'); } else { $li.removeClass('in-checked-path'); } } }, true); }, /** * 点击li事件 * @param fun * @private */ _liClick: function (fun) { var leaf = this.leaf; var $li = this.$li; if (this.props.expandTrigger === "click" || leaf) { $li.click(fun); } else if (this.props.expandTrigger === "hover") { $li.mouseenter(fun); } }, setChildren: function (children) { this.childrenNode = children; }, getChildren: function () { return this.childrenNode; } }; function Cascader(config) { this.config = $.extend(true, { elem: '', //绑定元素 value: null, //预设值 options: [], //可选项数据源,键名可通过 Props 属性配置 empty: '暂无数据', //无匹配选项时的内容 placeholder: '请选择',//输入框占位文本 disabled: false, //是否禁用 clearable: false, //是否支持清空选项 showAllLevels: true, //输入框中是否显示选中值的完整路径 collapseTags: false, //多选模式下是否折叠Tag minCollapseTagsNumber: 1, //最小折叠标签数 separator: ' / ', //选项分隔符 filterable: false, //是否可搜索选项 filterMethod: function (node, keyword) { return node.path.some(function (node) { return node.label.indexOf(keyword) !== -1; }); }, //自定义搜索逻辑,第一个参数是节点node,第二个参数是搜索关键词keyword,通过返回布尔值表示是否命中 debounce: 300, //搜索关键词输入的去抖延迟,毫秒 beforeFilter: function (value) { return true; },//筛选之前的钩子,参数为输入的值,若返回 false,则停止筛选 // popperClass: '', // 自定义浮层类名 string extendClass: false, //继承class样式 extendStyle: false, //继承style样式 props: { expandTrigger: 'click', //次级菜单的展开方式 string click / hover 'click' multiple: false, //是否多选 boolean - false checkStrictly: false, //是否严格的遵守父子节点不互相关联 boolean - false // lazy: false, //是否动态加载子节点,需与 lazyLoad 方法结合使用 boolean - false // lazyLoad: function (node, resolve) { // }, //加载动态数据的方法,仅在 lazy 为 true 时有效 function(node, resolve),node为当前点击的节点,resolve为数据加载完成的回调(必须调用) value: 'value', //指定选项的值为选项对象的某个属性值 string — 'value' label: 'label', //指定选项标签为选项对象的某个属性值 string — 'label' children: 'children', //指定选项的子选项为选项对象的某个属性值 string — 'children' disabled: 'disabled', //指定选项的禁用为选项对象的某个属性值 string — 'disabled' leaf: 'leaf' //指定选项的叶子节点的标志位为选项对象的某个属性值 string — 'leaf' } }, config); this.data = { value: null, checkedValue: [], checkedNodePaths: [], nodes: [], activeNode: null }; // 面板是否展开 this.showPanel = false; // 值变更事件 this.changeEvent = []; // 是否正在搜索中 this.filtering = false; // 初始化 this._init(); } Cascader.prototype = { constructor: Cascader, get props() { return this.config.props; }, get isFiltering() { return this.filtering; }, set isFiltering(filtering) { if (this.filtering === filtering) { return; } this.filtering = !!filtering; var $panel = this.$panel; if (this.filtering) { $panel.find('.el-cascader-panel').hide(); $panel.find('.el-cascader__suggestion-panel').show(); } else { $panel.find('.el-cascader-panel').show(); $panel.find('.el-cascader__suggestion-panel').hide(); this.$tagsInput && this.$tagsInput.val('') } }, icons: { from: 'layui-icon', down: 'layui-icon-down', close: 'layui-icon-close', right: 'layui-icon-right', ok: 'layui-icon-ok' }, // 初始化 _init: function () { if (!this.config.elem) { throw "缺少elem节点选择器"; } // 初始化输入框 this._initInput(); // 初始化节点 this.data.nodes = this.initNodes(this.config.options, 0, null); if (this.config.value) { this.setValue(this.config.value); } // 初始化面板 this._initPanel(); var self = this; // 监听滚动条 $(window).scroll(function () { self._resetXY(); }); // 监听窗口 $(window).resize(function () { self._resetXY(); }); // 点击事件,展开面板 this.$div.click(function (event) { if (self.config.disabled) { return; } // 阻止事件冒泡 event.stopPropagation(); var show = self.showPanel; if (!show) { self.focus(event); } else { self.blur(event); } }); }, // 面板定位 _resetXY: function () { var $div = this.$div; var offset = $div.offset(); var $panel = this.$panel; if ($panel) { var windowHeight = $(window).height(); var panelHeight = $panel.height(); var divHeight = $div.height(); var boundingClientRect = $div[0].getBoundingClientRect(); var bottomHeight = windowHeight - (boundingClientRect.top + divHeight); if (bottomHeight < panelHeight && boundingClientRect.top > panelHeight + 20) { $panel.attr('x-placement', 'top-start') // 向上 $panel.css({ top: offset.top - 20 - panelHeight + 'px', left: offset.left + 'px' }); } else { $panel.attr('x-placement', 'bottom-start') // 向下 $panel.css({ top: offset.top + divHeight + 'px', left: offset.left + 'px' }); } } }, get $menus() { return this.$panel && this.$panel.find('.el-cascader-panel .el-cascader-menu'); }, // 初始化输入框 _initInput: function () { var $e = $(this.config.elem); var self = this; // 当绑定的元素带有value属性,并且对象未设置值时,设置一个初始值 if (this.config.value === null && $e.attr('value')) { this.config.value = $e.attr('value'); } var placeholder = this.config.placeholder; var fromIcon = this.icons.from; var downIcon = this.icons.down; var multiple = this.props.multiple; var extendClass = this.config.extendClass; var extendStyle = this.config.extendStyle; this.$div = $('
    '); if (extendStyle) { var style = $e.attr('style'); if (style) { this.$div.attr('style', style); } } if (extendClass) { var className = $e.attr('class'); if (className) { className.split(' ').forEach(function (name) { self.$div.addClass(name); }); } } this.$input = $('
    ' + '' + '' + '' + '' + '' + '
    ') this.$div.append(this.$input); this.$inputRow = this.$input.find('.el-input__inner'); // 多选标签 if (multiple) { this.$tags = $('
    '); this.$div.append(this.$tags); } this._initHideElement($e); // 替换元素 $e.replaceWith(this.$div); this.$icon = this.$input.find('i'); this.disabled(this.config.disabled); this._initFilterableInputEvent(); }, /** * 初始化隐藏元素input,主要用于layui的表单验证 * @param $e * @private */ _initHideElement: function ($e) { // 保存原始元素 var $ec = $e.clone(); this.$ec = $ec; $ec.hide(); $e.before($ec); }, /** * 初始化可搜索监听事件 * @private */ _initFilterableInputEvent: function () { var filterable = this.config.filterable; if (!filterable) { return; } var timeoutID; var multiple = this.props.multiple; var debounce = this.config.debounce; var placeholder = this.config.placeholder; var beforeFilter = this.config.beforeFilter; var filterMethod = this.config.filterMethod; var checkStrictly = this.props.checkStrictly; var self = this; function filter(event) { var input = this; if (timeoutID) { clearTimeout(timeoutID); } timeoutID = setTimeout(function () { timeoutID = null; var val = $(input).val(); if (!val) { self.isFiltering = false; return; } self.focus(event); if (typeof beforeFilter === 'function' && beforeFilter(val)) { self.isFiltering = true; var nodes = self.getNodes(); var filterNodes = nodes.filter(function (node) { if (node.leaf || checkStrictly) { if (typeof filterMethod === 'function' && filterMethod(node, val)) { // 命中 return true; } } return false; }); self._setSuggestionMenu(filterNodes); } }, debounce); } if (multiple) { // 多选可搜索 this.$tagsInput = $(''); var $tagsInput = this.$tagsInput; this.$tags.append($tagsInput); $tagsInput.on('keydown', filter); $tagsInput.click(function (event) { if (self.isFiltering) { event.stopPropagation(); } }); } else { var $inputRow = this.$inputRow; // 单选可搜索 $inputRow.removeAttr('readonly'); $inputRow.on('keydown', filter); $inputRow.click(function (event) { if (self.isFiltering) { event.stopPropagation(); } }); } }, // 初始化面板(panel(1)) _initPanel: function () { var $panel = this.$panel; if (!$panel) { // z-index:解决和layer.open默认19891014的冲突 this.$panel = $(''); $panel = this.$panel; $panel.appendTo('body'); this._appendMenu(this.data.nodes, 0); $panel.click(function (event) { // 阻止事件冒泡 event.stopPropagation(); }); // 初始化可搜索面板 this._initSuggestionPanel(); } }, /** * 添加菜单(panel(1)->menu(n)) * @param nodes 当前层级数据 * @param level * @param parentNode * @private */ _appendMenu: function (nodes, level, parentNode) { var $div = $(''); // 除了上一层的所有菜单全部移除 var number = level - 1; if (number !== -1) { this.$panel.find('.el-cascader-panel .el-cascader-menu:gt(' + number + ')').remove(); } else { this.$panel.find('.el-cascader-panel .el-cascader-menu').remove(); } if (parentNode && parentNode.leaf) { return; } // 重新添加菜单 this.$panel.find('.el-cascader-panel').append($div); // 渲染细项 this._appendLi($div, nodes); // 渲染滚动条 this._initScrollbar($div); }, /** * 添加细项(panel(1)->menu(n)->li(n)) * @param $menu 当前菜单对象 * @param nodes 当前层级数据 * @private */ _appendLi: function ($menu, nodes) { var $list = $menu.find('.el-cascader-menu__list'); if (!nodes || nodes.length === 0) { var isEmpty = this.config.empty; $list.append('
    ' + isEmpty + '
    '); return; } $.each(nodes, function (index, node) { node.bind($list); }); }, /** * 初始化可搜索面板 * @private */ _initSuggestionPanel: function () { var filterable = this.config.filterable; if (!filterable) { return; } var $suggestionPanel = this.$suggestionPanel; if (!$suggestionPanel) { this.$suggestionPanel = $(''); $suggestionPanel = this.$suggestionPanel; this.$panel.find('.popper__arrow').before($suggestionPanel); $suggestionPanel.click(function (event) { // 阻止事件冒泡 event.stopPropagation(); }); } }, /** * 设置可搜索菜单 * @param nodes * @private */ _setSuggestionMenu: function (nodes) { var $suggestionPanel = this.$suggestionPanel; var $list = $suggestionPanel.find('.el-cascader__suggestion-list'); $list.empty(); $suggestionPanel.find('.el-scrollbar__bar').remove(); if (!nodes || nodes.length === 0) { $list.append('
  • 无匹配数据
  • '); return; } $.each(nodes, function (index, node) { node.bindSuggestion($list); }); this._initScrollbar($suggestionPanel); this._resetXY(); }, /** * 初始化节点数据 * @param data 原始数据 * @param level 层级 * @param parentNode 父级节点 * @returns {*[]} */ initNodes: function (data, level, parentNode) { var nodes = []; for (var i = 0; i < data.length; i++) { var datum = data[i]; var node = new Node(datum, this, level, parentNode); nodes.push(node); if (datum.children && datum.children.length > 0) { node.setChildren(this.initNodes(datum.children, level + 1, node)) } } // Change: 当数组原型被修改后使用in循环会导致取值错误 // for (var key in data) { // var datum = data[key]; // var node = new Node(datum, this, level, parentNode); // nodes.push(node); // if (datum.children && datum.children.length > 0) { // node.setChildren(this.initNodes(datum.children, level + 1, node)) // } // } return nodes; }, /** * 设置值 * @param value */ setValue: function (value) { if (!value) { return; } if (this.data.value || this.data.checkedValue.length > 0) { // 清空值 this.clearCheckedNodes(); } var nodes = this.getNodes(this.data.nodes); var checkStrictly = this.props.checkStrictly; var multiple = this.props.multiple; if (multiple) { nodes.forEach(function (node) { var leaf = node.leaf; if (value.indexOf(node.value) !== -1) { if (checkStrictly || leaf) { node.checked = 1; if (!checkStrictly) { node._syncTransferParent(); } } } }); } else { var filter = nodes.filter(function (node) { var leaf = node.leaf; if (checkStrictly || leaf) { if (node.value == value) { return true; } } return false; }); if (filter.length > 0) { var node = filter[0]; this.data.value = node.value; this.data.activeNode = node; this.change(this.data.value, this.data.activeNode); this._setClear(); } } }, /** * 递归获取扁平的节点 * @param nodes * @param container * @returns {*[]} */ getNodes: function (nodes, container) { if (!container) { container = []; } if (!nodes) { nodes = this.data.nodes; } var self = this; nodes.forEach(function (node) { container.push(node); var children = node.getChildren(); if (children) { self.getNodes(children, container); } }); return container; }, // 初始化滚动条 _initScrollbar: function ($menu) { var $div = $('
    '); $menu.append($div); var vertical = $($div[1]).find('.el-scrollbar__thumb'); var onhoriztal = $($div[0]).find('.el-scrollbar__thumb'); var scrollbar = $menu.find('.el-scrollbar__wrap'); var $panel = this.$panel; var $lis = $menu.find('li'); var height = Math.max($panel.height(), $menu.height()); var hScale = (height - 6) / ($lis.height() * $lis.length); var wScale = $panel.width() / $lis.width(); // 滚动条监听事件 function _scrollbarEvent($scroll) { if (hScale < 1) { vertical.css('height', hScale * 100 + '%'); vertical.css('transform', 'translateY(' + $scroll.scrollTop() / $menu.height() * 100 + '%)'); } if (wScale < 1) { onhoriztal.css('width', wScale * 100 + '%'); onhoriztal.css('transform', 'translateY(' + $scroll.scrollLeft() / $menu.width() * 100 + '%)'); } } // 拖动事件 vertical.mousedown(function (event) { event.stopImmediatePropagation(); event.stopPropagation(); // 禁止文本选择事件 var selectstart = function () { return false; }; $(document).bind("selectstart", selectstart); var y = event.clientY; var scrollTop = scrollbar.scrollTop(); // 移动事件 var mousemove = function (event) { event.stopImmediatePropagation(); var number = scrollTop + (event.clientY - y) / hScale; scrollbar.scrollTop(number); }; $(document).bind('mousemove', mousemove); // 鼠标松开事件 $(document).one('mouseup', function (event) { event.stopPropagation(); event.stopImmediatePropagation(); $(document).off('mousemove', mousemove); $(document).off('selectstart', selectstart); }); }); // 监听滚动条事件 scrollbar.scroll(function () { _scrollbarEvent($(this)); }); // 初始化滚动条 _scrollbarEvent(scrollbar); }, // 填充路径 _fillingPath: function () { var multiple = this.props.multiple; var showAllLevels = this.config.showAllLevels; var separator = this.config.separator; var collapseTags = this.config.collapseTags; var $inputRow = this.$inputRow; var placeholder = this.config.placeholder; var self = this; if (!multiple) { var path = this.data.activeNode && this.data.activeNode.path || []; if (showAllLevels) { this._$inputRowSetValue(path.map(function (node) { return node.label; }).join(separator)); } else { this._$inputRowSetValue(path[path.length - 1].label); } } else { // 复选框 // 删除标签 this.$tags.find('.el-tag').remove(); var $tagsInput = this.$tagsInput; // 清除高度 $inputRow.css('height', ''); var checkedNodePaths = this.data.checkedNodePaths; var minCollapseTagsNumber = Math.max(this.config.minCollapseTagsNumber, 1); if (checkedNodePaths.length > 0) { var tags = []; var paths = checkedNodePaths; if (collapseTags) { // 折叠tags paths = checkedNodePaths.slice(0, Math.min(checkedNodePaths.length, minCollapseTagsNumber)); } paths.forEach(function (node) { tags.push(node.$tag); }); // 判断标签是否折叠 if (collapseTags) { // 判断标签最小折叠数 if (checkedNodePaths.length > minCollapseTagsNumber) { tags.push(self.get$tag('+ ' + (checkedNodePaths.length - minCollapseTagsNumber), false)); } } tags.forEach(function (tag) { if ($tagsInput) { $tagsInput.before(tag) } else { self.$tags.append(tag); } }); } var tagHeight = self.$tags.height(); var inputHeight = $inputRow.height(); if (tagHeight > inputHeight) { $inputRow.css('height', tagHeight + 4 + 'px'); } // 重新定位 this._resetXY(); if (checkedNodePaths.length > 0) { $inputRow.removeAttr('placeholder'); $tagsInput && $tagsInput.removeAttr('placeholder', placeholder); } else { $inputRow.attr('placeholder', placeholder); $tagsInput && $tagsInput.attr('placeholder', placeholder); } } }, // 设置单选输入框的值 _$inputRowSetValue: function (value) { value = value || ""; var $inputRow = this.$inputRow; $inputRow.attr('value', value); //防止被重置 $inputRow.val(value); }, /** * 获取复选框标签对象 * @param label * @param showCloseIcon 是否显示关闭的icon * @returns {jQuery|HTMLElement|*} */ get$tag: function (label, showCloseIcon) { var fromIcon = this.icons.from; var closeIcon = this.icons.close; var icon = showCloseIcon ? '' : ''; return $('' + label + '' + icon + ''); }, // 设置可清理 _setClear: function () { if (this.config.clearable) { var self = this; function enter() { self.$icon.removeClass(self.icons.down); self.$icon.addClass(self.icons.close); } function out() { self.$icon.removeClass(self.icons.close); self.$icon.addClass(self.icons.down); } self.$div.mouseenter(function () { enter(); }); self.$div.mouseleave(function () { out(); }); self.$icon.one('click', function (event) { event.stopPropagation(); self.blur(event); self.clearCheckedNodes(); out(); self.$icon.off('mouseenter'); self.$div.off('mouseenter'); self.$div.off('mouseleave'); }); } }, // 禁用 disabled: function (isDisabled) { this.config.disabled = !!isDisabled; this.$input.attr('disabled', this.config.disabled ? 'disabled' : ''); if (this.config.disabled) { this.$div.addClass('is-disabled'); this.$div.find('.el-input--suffix').addClass('is-disabled'); } else { this.$div.removeClass('is-disabled'); this.$div.find('.el-input--suffix').removeClass('is-disabled'); } }, /** * 当选中节点变化时触发 选中节点的值 * @param value 值 * @param node 节点 */ change: function (value, node) { var multiple = this.props.multiple; if (multiple) { if (value && value.length > 0) { this.$ec.attr('value', JSON.stringify(value)); // this.$ec.val(JSON.stringify(value)); } else { this.$ec.removeAttr('value'); // this.$ec.val(''); } } else { this.$ec.attr('value', value || ""); // this.$ec.val(value); } // 填充路径 this._fillingPath(); this.changeEvent.forEach(function (callback) { if (typeof callback === 'function') { callback(value, node); } }); }, /** * 当失去焦点时触发 (event: Event) * @param event */ blur: function (event) { this.showPanel = false; this.$div.find('.layui-icon-down').removeClass('is-reverse'); this.$panel.slideUp(100); this.visibleChange(false); // 聚焦颜色 this.$input.removeClass('is-focus'); // 可搜索 var filterable = this.config.filterable; if (filterable) { this.isFiltering = false; this._fillingPath(); } }, /** * 当获得焦点时触发 (event: Event) * @param event */ focus: function (event) { this.showPanel = true; var self = this; // 点击背景关闭面板 $(document).one('click', function () { self.blur(event); }); // 重新定位面板 this._resetXY(); // 箭头icon翻转 this.$div.find('.layui-icon-down').addClass('is-reverse'); this.$panel.slideDown(200); this.visibleChange(true); // 聚焦颜色 this.$input.addClass('is-focus'); }, /** * 下拉框出现/隐藏时触发 * @param visible 出现则为 true,隐藏则为 false */ visibleChange: function (visible) { }, /** * 在多选模式下,移除Tag时触发 移除的Tag对应的节点的值 * @param tagValue 节点的值 * @param node 节点对象 */ removeTag: function (tagValue, node) { }, /** * 获取选中的节点值 * @returns {null|[]} */ getCheckedValues: function () { if (this.props.multiple) { return this.data.checkedValue; } else { return this.data.value; } }, /** * 获取选中的节点 * @returns {null|[]} */ getCheckedNodes: function () { if (this.props.multiple) { return this.data.checkedNodePaths; } else { return this.data.activeNode; } }, /** * 清空选中的节点 */ clearCheckedNodes: function () { this.data.value = null; this.data.activeNode = null; this.data.checkedValue = []; this.data.checkedNodePaths = []; var multiple = this.props.multiple; var $menus = this.$menus; if ($menus) { var $lis = $($menus[$menus.length - 1]).find('li'); if (multiple) { this.change(this.data.checkedValue, []); } else { $lis.find('.' + this.icons.ok).remove(); // 移除选中样式 $lis.removeClass('is-active'); this.change(this.data.value, null); } // 单选样式 this.$panel.find('.is-checked').removeClass('is-checked'); // 移除所有选中颜色 this.$panel.find('.in-checked-path').removeClass('in-checked-path'); // 移除最后一级粗体 $lis.removeClass('in-active-path'); // 移除复选框样式 var nodes = this.getNodes(); nodes.forEach(function (node) { node.checked = 0; }); } } }; var thisCas = function () { var self = this; return { /** * 覆盖当前值 * @param value 单选时传对象,多选时传数组 */ setValue: function (value) { self.setValue(value); }, /** * 当节点变更时,执行回调 * @param callback function(value,node){} */ change: function (callback) { self.changeEvent.push(callback); }, /** * 禁用组件 * @param disabled true/false */ disabled: function (disabled) { self.disabled.call(self, disabled); }, /** * 收起面板 */ blur: function () { self.blur.call(self); }, /** * 展开面板 */ focus: function () { self.focus.call(self); }, /** * 获取选中的节点,如需获取路径,使用node.path获取,将获取各级节点的node对象 * @returns {[]|*} */ getCheckedNodes: function () { return self.getCheckedNodes.call(self); }, /** * 获取选中的值 * @returns {[]|*} */ getCheckedValues: function () { return self.getCheckedValues.call(self); }, /** * 清空选中的节点 */ clearCheckedNodes: function () { self.clearCheckedNodes.call(self); } }; }; exports('layCascader', function (option) { var ins = new Cascader(option); return thisCas.call(ins); }); });