/**
* 仿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('');
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);
});
});