2120 lines
67 KiB
JavaScript
2120 lines
67 KiB
JavaScript
/**
|
||
* 仿element-ui,级联选择器
|
||
* 已实现单选、多选、无关联选择
|
||
* 其他功能:组件禁用、节点禁用、自定义属性、自定义空面板提示,自定义无选择时的提示、多选标签折叠、回显、搜索、动态加载、最大选中数量限制、禁用项固定等操作。
|
||
* element-ui没有的功能:最大选中数量限制、禁用项固定
|
||
* author: yixiaco
|
||
* gitee: https://gitee.com/yixiacoco/lay_cascader
|
||
* github: https://github.com/yixiaco/lay_cascader
|
||
*/
|
||
;!function (e) {
|
||
var src = e.document.currentScript.src;
|
||
var groupMatch = /(\S*\/)cascader(\.min)?\.js(\?.*)?/i.exec(src);
|
||
if (groupMatch && groupMatch.length >= 2 && (!groupMatch[3] || groupMatch[3].indexOf('css=true') !== -1)) {
|
||
layui.link(groupMatch[1] + 'cascader' + (groupMatch[2] || '') + '.css');
|
||
}
|
||
}(window);
|
||
layui.define(["jquery"], function (exports) {
|
||
var $ = layui.jquery;
|
||
|
||
/**
|
||
* 阻止事件冒泡
|
||
* @param event
|
||
*/
|
||
function stopPropagation(event) {
|
||
event = event || window.event
|
||
if (event.stopPropagation) {
|
||
event.stopPropagation()
|
||
} else {
|
||
event.cancelBubble = true
|
||
}
|
||
}
|
||
|
||
/**
|
||
* 级联各项节点对象
|
||
* @param data 原始对象信息
|
||
* @param cascader 级联对象
|
||
* @param level 层级,从0开始
|
||
* @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;
|
||
// 引入的icon图标
|
||
this.icons = cascader.icons;
|
||
//该节点是否被选中 0:未选中,1:选中,2:不定
|
||
this._checked = 0;
|
||
// 是否正在加载中
|
||
this._loading = false;
|
||
// 每个Node的唯一标识
|
||
this.nodeId = cascader.data.nodeId++;
|
||
}
|
||
|
||
Node.prototype = {
|
||
constructor: Node,
|
||
/** 最顶级父节点 */
|
||
get topParentNode() {
|
||
return !this.parentNode && this || this.topParentNode;
|
||
},
|
||
/** 子节点 */
|
||
childrenNode: undefined,
|
||
get loading() {
|
||
return this._loading;
|
||
},
|
||
set loading(loading) {
|
||
var $li = this.$li;
|
||
if ($li) {
|
||
var rightIcon = this.icons.right;
|
||
var loadingIcon = this.icons.loading;
|
||
var $i = $li.find('i');
|
||
if (loading) {
|
||
$i.addClass(loadingIcon);
|
||
$i.removeClass(rightIcon);
|
||
} else {
|
||
$i.addClass(rightIcon);
|
||
$i.removeClass(loadingIcon);
|
||
}
|
||
}
|
||
return this._loading = loading;
|
||
},
|
||
/** 当前节点的显示文本 */
|
||
get label() {
|
||
return this.data[this.props.label];
|
||
},
|
||
/** 当前节点的值 */
|
||
get value() {
|
||
return this.data[this.props.value];
|
||
},
|
||
/** 是否禁用 */
|
||
get disabled() {
|
||
var multiple = this.props.multiple;
|
||
var maxSize = this.config.maxSize;
|
||
var checkedNodeIds = this.cascader.data.checkedNodeIds;
|
||
var disabledName = this.props.disabled;
|
||
var checkStrictly = this.props.checkStrictly;
|
||
// 检查是否超过最大值限制
|
||
if (multiple && maxSize !== 0) {
|
||
if (checkedNodeIds.length >= maxSize && checkedNodeIds.indexOf(this.nodeId) === -1) {
|
||
// 如果是关联的多选,需要查询叶子节点是否有被选中的项
|
||
if (!checkStrictly) {
|
||
var leafChildren = this.getAllLeafChildren();
|
||
var nodeIds = leafChildren.map(function (value) {
|
||
return value.nodeId
|
||
});
|
||
// 如果叶子节点不包含,则直接返回true
|
||
if (!nodeIds.some(function (nodeId) {
|
||
return checkedNodeIds.indexOf(nodeId) !== -1;
|
||
})) {
|
||
return true;
|
||
}
|
||
} else {
|
||
return true;
|
||
}
|
||
}
|
||
}
|
||
if (!checkStrictly) {
|
||
var path = this.path;
|
||
return path.some(function (node) {
|
||
return node.data[disabledName];
|
||
});
|
||
} else {
|
||
return this.data[disabledName];
|
||
}
|
||
},
|
||
/** 子节点数据 */
|
||
get children() {
|
||
return this.data[this.props.children];
|
||
},
|
||
set children(children) {
|
||
this.data[this.props.children] = 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 activeNodeId() {
|
||
return this.cascader.data.activeNodeId;
|
||
},
|
||
/** 当前复选框值 */
|
||
get checkedNodeIds() {
|
||
return this.cascader.data.checkedNodeIds;
|
||
},
|
||
/** 路径 */
|
||
get path() {
|
||
var parentNode = this.parentNode;
|
||
if (parentNode) {
|
||
return parentNode.path.concat([this]);
|
||
} else {
|
||
return [this];
|
||
}
|
||
},
|
||
/** 是否正在搜索中 */
|
||
get isFiltering() {
|
||
return this.cascader.isFiltering;
|
||
},
|
||
/** 输入框的tag标签 */
|
||
get $tag() {
|
||
var cascader = this.cascader;
|
||
var showAllLevels = this.config.showAllLevels;
|
||
var disabled = this.config.disabled;
|
||
var nodeDisabled = this.disabled;
|
||
var disabledFixed = this.config.disabledFixed;
|
||
|
||
var label = this.getPathLabel(showAllLevels);
|
||
var $tag = cascader.get$tag(label, !disabled && (!nodeDisabled || !disabledFixed));
|
||
var self = this;
|
||
$tag.find('i').click(function (event) {
|
||
stopPropagation(event);
|
||
self.selectedValue();
|
||
cascader.removeTag(self.value, self);
|
||
});
|
||
return $tag;
|
||
},
|
||
/**
|
||
* 完整路径的标签
|
||
* @param showAllLevels
|
||
* @returns {string}
|
||
*/
|
||
getPathLabel: function (showAllLevels) {
|
||
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 = $('<li role="menuitem" id="cascader-menu" tabindex="-1" class="el-cascader-node" aria-haspopup="true" aria-owns="cascader-menu"><span class="el-cascader-node__label">' + label + '</span><i class="' + fromIcon + ' ' + icon + '"></i></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(true);
|
||
this.$suggestionLi = $('<li tabindex="-1" class="el-cascader__suggestion-item"><span>' + label + '</span></li>');
|
||
// 节点渲染
|
||
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 nodeId = this.nodeId;
|
||
var fromIcon = this.icons.from;
|
||
var okIcon = this.icons.ok;
|
||
var self = this;
|
||
var cascader = this.cascader;
|
||
var multiple = this.props.multiple;
|
||
|
||
var icon = '<i class="' + fromIcon + ' ' + okIcon + ' el-icon-check"></i>';
|
||
$li.click(function (event) {
|
||
stopPropagation(event);
|
||
self.selectedValue();
|
||
if (multiple) {
|
||
if (self.checkedNodeIds.indexOf(nodeId) === -1) {
|
||
$li.removeClass('is-checked');
|
||
$li.find('.el-icon-check').remove();
|
||
} else {
|
||
$li.addClass('is-checked');
|
||
$li.append(icon);
|
||
}
|
||
} else {
|
||
// 关闭面板
|
||
cascader.close();
|
||
}
|
||
});
|
||
|
||
if (multiple && self.checkedNodeIds.indexOf(nodeId) !== -1
|
||
|| !multiple && self.activeNodeId === nodeId) {
|
||
$li.addClass('is-checked');
|
||
$li.append(icon)
|
||
}
|
||
},
|
||
/**
|
||
* 单选&&关联
|
||
* @private
|
||
*/
|
||
_renderRadio: function () {
|
||
var $li = this.$li;
|
||
var nodeId = this.nodeId;
|
||
var fromIcon = this.icons.from;
|
||
var okIcon = this.icons.ok;
|
||
var level = this.level;
|
||
var leaf = this.leaf;
|
||
var self = this;
|
||
var cascader = this.cascader;
|
||
var activeNode = this.cascader.data.activeNode;
|
||
var parentNode = this.parentNode;
|
||
|
||
if (self.activeNodeId && activeNode.path.some(function (node) {
|
||
return node.nodeId === nodeId;
|
||
})) {
|
||
if (self.activeNodeId === nodeId) {
|
||
$li.prepend('<i class="' + fromIcon + ' ' + okIcon + ' el-cascader-node__prefix"></i>');
|
||
}
|
||
$li.addClass('is-active');
|
||
$li.addClass('in-checked-path');
|
||
}
|
||
|
||
// 是否禁用
|
||
if (this.disabled) {
|
||
$li.addClass('is-disabled');
|
||
return;
|
||
}
|
||
|
||
$li.addClass('is-selectable');
|
||
|
||
if (parentNode) {
|
||
parentNode.$li.siblings().removeClass('in-active-path');
|
||
parentNode.$li.addClass('in-active-path');
|
||
}
|
||
|
||
// 触发下一个节点
|
||
this._liClick(function (event) {
|
||
stopPropagation(event);
|
||
var childrenNode = self.childrenNode;
|
||
if (leaf && event.type === 'click') {
|
||
self.selectedValue();
|
||
// 关闭面板
|
||
cascader.close();
|
||
}
|
||
// 添加下级菜单
|
||
cascader._appendMenu(childrenNode, level + 1, self);
|
||
});
|
||
},
|
||
/**
|
||
* 单选&&非关联
|
||
* @private
|
||
*/
|
||
_renderRadioCheckStrictly: function () {
|
||
var $li = this.$li;
|
||
var nodeId = this.nodeId;
|
||
var level = this.level;
|
||
var leaf = this.leaf;
|
||
var self = this;
|
||
var cascader = this.cascader;
|
||
var activeNode = cascader.data.activeNode;
|
||
var parentNode = this.parentNode;
|
||
|
||
$li.addClass('is-selectable');
|
||
// 任意一级单选
|
||
var $radio = $('<label role="radio" tabindex="0" class="el-radio"><span class="el-radio__input"><span class="el-radio__inner"></span><input type="radio" aria-hidden="true" tabindex="-1" class="el-radio__original" value="' + nodeId + '"></span><span class="el-radio__label"><span></span></span></label>');
|
||
this.$radio = $radio;
|
||
$li.prepend($radio);
|
||
if (parentNode) {
|
||
parentNode.$li.siblings().removeClass('in-active-path');
|
||
parentNode.$li.addClass('in-active-path');
|
||
}
|
||
|
||
// 触发下一个节点
|
||
this._liClick(function (event) {
|
||
stopPropagation(event);
|
||
var childrenNode = self.childrenNode;
|
||
if (!self.disabled && leaf && event.type === 'click') {
|
||
self.selectedValue();
|
||
}
|
||
// 添加下级菜单
|
||
cascader._appendMenu(childrenNode, level + 1, self);
|
||
});
|
||
|
||
if (self.activeNodeId && activeNode.path.some(function (node) {
|
||
return node.nodeId === nodeId;
|
||
})) {
|
||
if (self.activeNodeId === nodeId) {
|
||
$radio.find('.el-radio__input').addClass('is-checked');
|
||
}
|
||
$li.addClass('is-active');
|
||
$li.addClass('in-checked-path');
|
||
}
|
||
|
||
if (this.disabled) {
|
||
$radio.addClass('is-disabled');
|
||
$radio.find('.el-radio__input').addClass('is-disabled');
|
||
return;
|
||
}
|
||
// 选中事件
|
||
$radio.click(function (event) {
|
||
event.preventDefault();
|
||
!leaf && self.selectedValue();
|
||
});
|
||
},
|
||
/**
|
||
* 多选&&关联
|
||
* @private
|
||
*/
|
||
_renderMultiple: function () {
|
||
var $li = this.$li;
|
||
var level = this.level;
|
||
var leaf = this.leaf;
|
||
var self = this;
|
||
var cascader = this.cascader;
|
||
var checked = this._checked;
|
||
var parentNode = this.parentNode;
|
||
|
||
$li.addClass('el-cascader-node');
|
||
|
||
// 多选框
|
||
var $checked = $('<label class="el-checkbox"><span class="el-checkbox__input"><span class="el-checkbox__inner"></span><input type="checkbox" aria-hidden="false" class="el-checkbox__original" value=""></span></label>');
|
||
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 (parentNode) {
|
||
parentNode.$li.siblings().removeClass('in-active-path');
|
||
parentNode.$li.addClass('in-active-path');
|
||
}
|
||
|
||
// 触发下一个节点
|
||
this._liClick(function (event) {
|
||
stopPropagation(event);
|
||
var childrenNode = self.childrenNode;
|
||
if (!self.disabled && leaf && event.type === 'click') {
|
||
// 最后一级就默认选择
|
||
self.selectedValue();
|
||
}
|
||
// 添加下级菜单
|
||
cascader._appendMenu(childrenNode, level + 1, self);
|
||
});
|
||
|
||
if (this.disabled) {
|
||
$li.addClass('is-disabled');
|
||
$checked.addClass('is-disabled');
|
||
$checked.find('.el-checkbox__input').addClass('is-disabled');
|
||
return;
|
||
}
|
||
|
||
// 选中事件
|
||
$checked.click(function (event) {
|
||
event.preventDefault();
|
||
if (!leaf) {
|
||
var childrenNode = self.childrenNode;
|
||
self.selectedValue();
|
||
cascader._appendMenu(childrenNode, level + 1, self);
|
||
}
|
||
});
|
||
},
|
||
/**
|
||
* 多选&&非关联
|
||
* @private
|
||
*/
|
||
_renderMultipleCheckStrictly: function () {
|
||
var $li = this.$li;
|
||
var level = this.level;
|
||
var leaf = this.leaf;
|
||
var self = this;
|
||
var cascader = this.cascader;
|
||
var checkedNodeIds = cascader.data.checkedNodeIds;
|
||
var checkedNodes = cascader.data.checkedNodes;
|
||
var nodeId = this.nodeId;
|
||
var parentNode = this.parentNode;
|
||
|
||
$li.addClass('el-cascader-node is-selectable');
|
||
|
||
// 多选框
|
||
var $checked = $('<label class="el-checkbox"><span class="el-checkbox__input"><span class="el-checkbox__inner"></span><input type="checkbox" aria-hidden="false" class="el-checkbox__original" value=""></span></label>');
|
||
this.$checked = $checked;
|
||
$li.prepend($checked);
|
||
|
||
// 渲染
|
||
var exist = checkedNodes.some(function (node) {
|
||
return node.path.some(function (node) {
|
||
return node.nodeId === nodeId;
|
||
})
|
||
});
|
||
if (exist) {
|
||
$li.addClass('in-checked-path');
|
||
if (checkedNodeIds.indexOf(nodeId) !== -1) {
|
||
this.$checked.find('.el-checkbox__input').addClass('is-checked');
|
||
}
|
||
}
|
||
|
||
if (parentNode) {
|
||
parentNode.$li.siblings().removeClass('in-active-path');
|
||
parentNode.$li.addClass('in-active-path');
|
||
}
|
||
|
||
// 触发下一个节点
|
||
this._liClick(function (event) {
|
||
stopPropagation(event);
|
||
var childrenNode = self.childrenNode;
|
||
if (!self.disabled && leaf && event.type === 'click') {
|
||
// 最后一级就默认选择
|
||
self.selectedValue();
|
||
}
|
||
// 添加下级菜单
|
||
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();
|
||
if (!leaf) {
|
||
self.selectedValue();
|
||
var childrenNode = self.childrenNode;
|
||
// 添加下级菜单
|
||
cascader._appendMenu(childrenNode, level + 1, self);
|
||
}
|
||
});
|
||
},
|
||
/**
|
||
* 向上传递
|
||
* @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);
|
||
}
|
||
}
|
||
},
|
||
/**
|
||
* 设置级联值
|
||
*/
|
||
selectedValue: function () {
|
||
var nodeId = this.nodeId;
|
||
var cascader = this.cascader;
|
||
var multiple = this.props.multiple;
|
||
var checkStrictly = this.props.checkStrictly;
|
||
var leaf = this.leaf;
|
||
if (!multiple && (leaf || checkStrictly)) {
|
||
cascader._setActiveValue(nodeId, this);
|
||
} else if (multiple) {
|
||
var checkedNodeIds = cascader.data.checkedNodeIds;
|
||
var checkedNodes = cascader.data.checkedNodes;
|
||
var disabledFixed = this.config.disabledFixed;
|
||
var paths;
|
||
if (checkStrictly) {
|
||
var index = checkedNodeIds.indexOf(nodeId);
|
||
if (index === -1) {
|
||
paths = checkedNodes.concat([this]);
|
||
} else {
|
||
paths = checkedNodes.concat();
|
||
paths.splice(index, 1);
|
||
}
|
||
} else {
|
||
var allLeafChildren = this.getAllLeafChildren();
|
||
var checked;
|
||
if (this._checked !== 1 && disabledFixed) {
|
||
checked = this._getMultipleChecked(allLeafChildren);
|
||
} else {
|
||
checked = this._checked;
|
||
}
|
||
if (checked === 1) {
|
||
// 选中->未选中
|
||
paths = checkedNodes.filter(function (node1) {
|
||
return !allLeafChildren.some(function (node2) {
|
||
return node1.nodeId === node2.nodeId;
|
||
});
|
||
});
|
||
} else {
|
||
// 未选中、部分选中->选中
|
||
var add = allLeafChildren.filter(function (node) {
|
||
return checkedNodeIds.indexOf(node.nodeId) === -1;
|
||
});
|
||
paths = checkedNodes.concat(add);
|
||
}
|
||
}
|
||
var nodeIds = paths.map(function (node) {
|
||
return node.nodeId;
|
||
});
|
||
cascader._setCheckedValue(nodeIds, paths);
|
||
}
|
||
},
|
||
_liLoad: function (event, callback) {
|
||
var leaf = this.leaf;
|
||
var lazy = this.props.lazy;
|
||
var lazyLoad = this.props.lazyLoad;
|
||
var children = this.children;
|
||
var self = this;
|
||
var cascader = this.cascader;
|
||
var level = this.level;
|
||
var multiple = this.props.multiple;
|
||
var checkStrictly = this.props.checkStrictly;
|
||
if (!leaf && (!children || children.length === 0) && lazy) {
|
||
if (!self.loading) {
|
||
self.loading = true;
|
||
lazyLoad(self, function (nodes) {
|
||
self.loading = false;
|
||
self.setChildren(cascader.initNodes(nodes, level + 1, self));
|
||
self.children = nodes;
|
||
callback && callback(event);
|
||
// 多选&关联时,重新同步下父级节点的样式
|
||
multiple && !checkStrictly && self.transferParent(function (node) {
|
||
node.syncStyle();
|
||
}, true);
|
||
});
|
||
}
|
||
} else {
|
||
callback && callback(event);
|
||
}
|
||
},
|
||
/**
|
||
* 点击li事件
|
||
* @param callback
|
||
* @private
|
||
*/
|
||
_liClick: function (callback) {
|
||
var leaf = this.leaf;
|
||
var $li = this.$li;
|
||
var self = this;
|
||
|
||
function load(event) {
|
||
self._liLoad(event, callback);
|
||
}
|
||
|
||
if (this.props.expandTrigger === "click" || leaf) {
|
||
$li.click(load);
|
||
}
|
||
if (this.props.expandTrigger === "hover") {
|
||
$li.mouseenter(load);
|
||
}
|
||
},
|
||
setChildren: function (children) {
|
||
this.childrenNode = children;
|
||
},
|
||
getChildren: function () {
|
||
return this.childrenNode;
|
||
},
|
||
/**
|
||
* 同步样式
|
||
*/
|
||
syncStyle: function () {
|
||
var multiple = this.props.multiple;
|
||
var checkStrictly = this.props.checkStrictly;
|
||
if (multiple) {
|
||
//多选
|
||
if (checkStrictly) {
|
||
this._sync.syncMultipleCheckStrictly(this);
|
||
} else {
|
||
this._sync.syncMultiple(this);
|
||
}
|
||
} else {
|
||
//单选
|
||
if (checkStrictly) {
|
||
this._sync.syncRadioCheckStrictly(this);
|
||
} else {
|
||
this._sync.syncRadio(this);
|
||
}
|
||
}
|
||
},
|
||
/**
|
||
* 同步本节点样式
|
||
*/
|
||
_sync: {
|
||
/**
|
||
* 同步单选关联样式
|
||
*/
|
||
syncRadio: function (self) {
|
||
var $li = self.$li;
|
||
var fromIcon = self.icons.from;
|
||
var okIcon = self.icons.ok;
|
||
var multiple = self.props.multiple;
|
||
var checkStrictly = self.props.checkStrictly;
|
||
var nodeId = self.nodeId;
|
||
if (!$li || multiple || checkStrictly) {
|
||
return;
|
||
}
|
||
var activeNode = self.cascader.data.activeNode;
|
||
if (self.activeNodeId === nodeId) {
|
||
var ok = $li.find('.' + okIcon);
|
||
if (ok.length === 0) {
|
||
$li.prepend('<i class="' + fromIcon + ' ' + okIcon + ' el-cascader-node__prefix"></i>');
|
||
}
|
||
} else {
|
||
$li.find('.' + okIcon).remove();
|
||
}
|
||
if (activeNode && activeNode.path.some(function (node) {
|
||
return node.nodeId === nodeId;
|
||
})) {
|
||
$li.addClass('is-active');
|
||
$li.addClass('in-checked-path');
|
||
} else {
|
||
$li.removeClass('is-active');
|
||
$li.removeClass('in-checked-path');
|
||
}
|
||
},
|
||
/**
|
||
* 同步单选非关联样式
|
||
*/
|
||
syncRadioCheckStrictly: function (self) {
|
||
var $li = self.$li;
|
||
var checkStrictly = self.props.checkStrictly;
|
||
var multiple = self.props.multiple;
|
||
if (!$li || multiple || !checkStrictly) {
|
||
return;
|
||
}
|
||
var $radio = self.$radio;
|
||
var activeNode = self.cascader.data.activeNode;
|
||
var nodeId = self.nodeId;
|
||
if (self.activeNodeId === nodeId) {
|
||
$radio.find('.el-radio__input').addClass('is-checked');
|
||
} else {
|
||
$radio.find('.el-radio__input').removeClass('is-checked');
|
||
}
|
||
if (activeNode && activeNode.path.some(function (node) {
|
||
return node.nodeId === nodeId;
|
||
})) {
|
||
$li.addClass('is-active');
|
||
$li.addClass('in-checked-path');
|
||
} else {
|
||
$li.removeClass('is-active');
|
||
$li.removeClass('in-checked-path');
|
||
}
|
||
},
|
||
/**
|
||
* 同步多选关联样式
|
||
*/
|
||
syncMultiple: function (self) {
|
||
var $li = self.$li;
|
||
var checkStrictly = self.props.checkStrictly;
|
||
var multiple = self.props.multiple;
|
||
var disabledFixed = self.config.disabledFixed;
|
||
if (!multiple || checkStrictly) {
|
||
return;
|
||
}
|
||
var allLeafChildren = self.getAllLeafChildren(disabledFixed);
|
||
// 全部未选中 0
|
||
// 全部选中 1
|
||
// 部分选中 2
|
||
var checked = self._getMultipleChecked(allLeafChildren);
|
||
self._checked = checked;
|
||
if (!$li) {
|
||
return;
|
||
}
|
||
var $checkbox = self.$checked.find('.el-checkbox__input');
|
||
if (checked === 0) {
|
||
$checkbox.removeClass('is-checked');
|
||
$checkbox.removeClass('is-indeterminate');
|
||
} else if (checked === 1) {
|
||
$checkbox.removeClass('is-indeterminate');
|
||
$checkbox.addClass('is-checked');
|
||
} else if (checked === 2) {
|
||
$checkbox.removeClass('is-checked');
|
||
$checkbox.addClass('is-indeterminate');
|
||
}
|
||
},
|
||
/**
|
||
* 同步多选非关联样式
|
||
*/
|
||
syncMultipleCheckStrictly: function (self) {
|
||
var $li = self.$li;
|
||
var checkStrictly = self.props.checkStrictly;
|
||
var multiple = self.props.multiple;
|
||
if (!$li || !multiple || !checkStrictly) {
|
||
return;
|
||
}
|
||
var checkedNodes = self.cascader.data.checkedNodes;
|
||
var checkedNodeIds = self.checkedNodeIds;
|
||
var nodeId = self.nodeId;
|
||
var exist = checkedNodes.some(function (node) {
|
||
return node.path.some(function (node) {
|
||
return node.nodeId === nodeId;
|
||
});
|
||
});
|
||
var $checkbox = self.$checked.find('.el-checkbox__input');
|
||
if (checkedNodeIds.some(function (checkedNodeId) {
|
||
return checkedNodeId === nodeId;
|
||
})) {
|
||
// 选中
|
||
$checkbox.addClass('is-checked');
|
||
} else {
|
||
// 未选中
|
||
$checkbox.removeClass('is-checked');
|
||
}
|
||
if (exist) {
|
||
$li.addClass('in-checked-path');
|
||
} else {
|
||
$li.removeClass('in-checked-path');
|
||
}
|
||
}
|
||
},
|
||
/**
|
||
* 获取叶子节点value值
|
||
* @param disabled 是否包含禁用,默认不包含
|
||
* @returns {Node[]|*[]}
|
||
*/
|
||
getAllLeafChildren: function (disabled) {
|
||
var leaf = this.leaf;
|
||
if (leaf) {
|
||
return [this];
|
||
} else {
|
||
var leafs = [];
|
||
this.transferChildren(function (node) {
|
||
if (node.disabled && !disabled) {
|
||
return false;
|
||
}
|
||
node.leaf && leafs.push(node);
|
||
});
|
||
return leafs;
|
||
}
|
||
},
|
||
/**
|
||
* 展开当前节点
|
||
*/
|
||
expandPanel: function () {
|
||
var path = this.path;
|
||
var cascader = this.cascader;
|
||
path.forEach(function (node, index, array) {
|
||
if (index !== array.length - 1) {
|
||
var childrenNode = node.childrenNode;
|
||
cascader._appendMenu(childrenNode, node.level + 1, node.parentNode);
|
||
}
|
||
});
|
||
},
|
||
_getMultipleChecked: function (leafNodes) {
|
||
var cascader = this.cascader;
|
||
var checkedNodeIds = cascader.data.checkedNodeIds;
|
||
var allLeafIds = leafNodes.map(function (node) {
|
||
return node.nodeId;
|
||
});
|
||
// 全部未选中 0
|
||
// 全部选中 1
|
||
// 部分选中 2
|
||
var checked = 0;
|
||
for (var i = 0; i < allLeafIds.length; i++) {
|
||
var child = allLeafIds[i];
|
||
if (checked === 2) {
|
||
break;
|
||
}
|
||
if (checkedNodeIds.indexOf(child) !== -1) {
|
||
if (i > 0 && checked !== 1) {
|
||
checked = 2;
|
||
} else {
|
||
checked = 1;
|
||
}
|
||
} else {
|
||
// 当全部选中时,则改为部分选中,否则全部未选中
|
||
checked = checked === 1 ? 2 : 0;
|
||
}
|
||
}
|
||
return checked;
|
||
}
|
||
};
|
||
|
||
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样式
|
||
disabledFixed: false, //固定禁用项,使禁用项不被清理删除,禁用项只能通过函数添加或初始值添加,默认禁用项不可被函数或初始值添加
|
||
maxSize: 0, // 多选选中的最大数量,0表示不限制
|
||
props: {
|
||
strictMode: false, //严格模式,设置value严格按照层级结构.例如:[[1,2,3],[1,2,4]]
|
||
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 = {
|
||
nodeId: 1, //nodeId的自增值
|
||
nodes: [], //存储Node对象
|
||
menuData: [], //压入菜单的数据
|
||
activeNodeId: null, //存放NodeId
|
||
activeNode: null, //存放Node
|
||
checkedNodeIds: [], //存放多个NodeId
|
||
checkedNodes: [] //存放多个Node
|
||
};
|
||
// 面板是否展开
|
||
this.showPanel = false;
|
||
this.event = {
|
||
// 值变更事件
|
||
change: [],
|
||
// 打开事件
|
||
open: [],
|
||
// 关闭事件
|
||
close: []
|
||
}
|
||
// 是否正在搜索中
|
||
this.filtering = false;
|
||
// 初始化
|
||
this._init();
|
||
// 面板关闭事件id
|
||
this.closeEventId = 0;
|
||
// 是否进入maxSize模式
|
||
this._maxSizeMode = null;
|
||
}
|
||
|
||
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('')
|
||
}
|
||
},
|
||
set maxSizeMode(maxSizeMode) {
|
||
if (this._maxSizeMode !== maxSizeMode) {
|
||
this._maxSizeMode = maxSizeMode;
|
||
this.refreshMenu();
|
||
}
|
||
},
|
||
icons: {
|
||
from: 'layui-icon',
|
||
down: 'layui-icon-down',
|
||
close: 'layui-icon-close',
|
||
right: 'layui-icon-right',
|
||
ok: 'layui-icon-ok',
|
||
loading: 'layui-icon-loading-1 layui-anim layui-anim-rotate layui-anim-loop'
|
||
},
|
||
// 初始化
|
||
_init: function () {
|
||
this._checkConfig();
|
||
// 初始化输入框
|
||
this._initInput();
|
||
// 初始化面板
|
||
this._initPanel();
|
||
// 初始化选项值
|
||
this.setOptions(this.config.options);
|
||
var self = this;
|
||
// 监听滚动条
|
||
$(window).scroll(function () {
|
||
self._resetXY();
|
||
});
|
||
// 监听窗口
|
||
$(window).resize(function () {
|
||
self._resetXY();
|
||
});
|
||
// 点击事件,展开面板
|
||
this.$div.click(function (event) {
|
||
if (self.config.disabled) {
|
||
return;
|
||
}
|
||
var show = self.showPanel;
|
||
if (!show) {
|
||
self.open();
|
||
} else {
|
||
self.close();
|
||
}
|
||
});
|
||
},
|
||
/**
|
||
* 检查配置
|
||
* @private
|
||
*/
|
||
_checkConfig: function () {
|
||
var elem = this.config.elem;
|
||
if (!elem || $(elem).length === 0) {
|
||
throw new Error("缺少elem节点选择器");
|
||
}
|
||
var maxSize = this.config.maxSize;
|
||
if (typeof maxSize !== 'number' || maxSize < 0) {
|
||
throw new Error("maxSize应是一个大于等于0的有效的number值");
|
||
}
|
||
if (!Array.isArray(this.config.options)) {
|
||
throw new Error("options不是一个有效的数组");
|
||
}
|
||
},
|
||
/**
|
||
* 初始化根目录
|
||
* @private
|
||
*/
|
||
_initRoot: function () {
|
||
var lazy = this.props.lazy;
|
||
var lazyLoad = this.props.lazyLoad;
|
||
var self = this;
|
||
var nodes = this.data.nodes;
|
||
if (nodes.length > 0 || !lazy) {
|
||
this._appendMenu(nodes, 0);
|
||
} else if (lazy) {
|
||
this._appendMenu(nodes, 0);
|
||
lazyLoad({
|
||
root: true,
|
||
level: 0
|
||
}, function (nodes) {
|
||
self.data.nodes = self.initNodes(nodes, 0, null);
|
||
self._appendMenu(self.data.nodes, 0);
|
||
});
|
||
}
|
||
},
|
||
/**
|
||
* 设置选项值
|
||
* @param options
|
||
*/
|
||
setOptions: function (options) {
|
||
this.config.options = options;
|
||
// 初始化节点
|
||
this.data.nodes = this.initNodes(options, 0, null);
|
||
// 初始化根目录
|
||
this._initRoot();
|
||
// 初始化值
|
||
this.setValue(this.config.value);
|
||
},
|
||
// 面板定位
|
||
_resetXY: function () {
|
||
var $div = this.$div;
|
||
var offset = $div.offset();
|
||
var $panel = this.$panel;
|
||
if ($panel) {
|
||
var windowHeight = window.innerHeight;
|
||
var windowWidth = window.innerWidth;
|
||
var panelHeight = $panel.height();
|
||
var panelWidth = $panel.width();
|
||
var divHeight = $div.height();
|
||
var boundingClientRect = $div[0].getBoundingClientRect();
|
||
var $arrow = $panel.find('.popper__arrow');
|
||
|
||
// 距离右边界的偏差值
|
||
var offsetDiff = Math.min(windowWidth - boundingClientRect.x - panelWidth - 5, 0);
|
||
|
||
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 + offsetDiff + 'px'
|
||
});
|
||
} else {
|
||
$panel.attr('x-placement', 'bottom-start');
|
||
// 距离底部边界的偏差值
|
||
var yOffset = Math.max(panelHeight - (windowHeight - boundingClientRect.y - divHeight - 15), 0);
|
||
// 向下
|
||
$panel.css({
|
||
top: offset.top + divHeight - yOffset + 'px',
|
||
left: offset.left + offsetDiff + 'px'
|
||
});
|
||
}
|
||
// 箭头偏移
|
||
$arrow.css("left", 35 - offsetDiff + "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 = $('<div class="el-cascader"></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 = $('<div class="el-input el-input--suffix">' +
|
||
'<input type="text" readonly="readonly" autocomplete="off" placeholder="' + placeholder + '" class="el-input__inner">' +
|
||
'<span class="el-input__suffix">' +
|
||
'<span class="el-input__suffix-inner">' +
|
||
'<i class="el-icon-arrow-down ' + fromIcon + ' ' + downIcon + '" style="font-size: 12px"></i>' +
|
||
'</span></span>' +
|
||
'</div>')
|
||
this.$div.append(this.$input);
|
||
this.$inputRow = this.$input.find('.el-input__inner');
|
||
// 多选标签
|
||
if (multiple) {
|
||
this.$tags = $('<div class="el-cascader__tags"><!----></div>');
|
||
this.$div.append(this.$tags);
|
||
}
|
||
var element = this._initHideElement($e);
|
||
// 在后面插入元素
|
||
element.after(this.$div);
|
||
this.$icon = this.$input.find('i');
|
||
this._initFilterableInputEvent();
|
||
this.disabled(this.config.disabled);
|
||
},
|
||
/**
|
||
* 初始化隐藏元素input,主要用于layui的表单验证
|
||
* @param $e
|
||
* @private
|
||
*/
|
||
_initHideElement: function ($e) {
|
||
var tagName = $e.prop("tagName").toLowerCase();
|
||
// 保存原始元素
|
||
if (tagName === 'input') {
|
||
$e.hide();
|
||
$e.attr('type', 'hidden')
|
||
this.$ec = $e;
|
||
return $e;
|
||
} else {
|
||
var attributes = $e[0].attributes;
|
||
var $input = $('<input />');
|
||
var keys = Object.keys(attributes);
|
||
for (var key in keys) {
|
||
var attribute = attributes[key];
|
||
$input.attr(attribute.name, attribute.value);
|
||
}
|
||
$input.hide();
|
||
$input.attr('type', 'hidden')
|
||
this.$ec = $input;
|
||
$e.before($input);
|
||
$e.remove();
|
||
return $input;
|
||
}
|
||
},
|
||
/**
|
||
* 初始化可搜索监听事件
|
||
* @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.open();
|
||
if (typeof beforeFilter === 'function' && beforeFilter(val)) {
|
||
self.isFiltering = true;
|
||
var nodes = self.getNodes();
|
||
var filterNodes = nodes.filter(function (node) {
|
||
var disabled;
|
||
if (checkStrictly) {
|
||
disabled = node.disabled;
|
||
} else {
|
||
disabled = node.path.some(function (node) {
|
||
return node.disabled;
|
||
});
|
||
}
|
||
if ((node.leaf || checkStrictly) && !disabled) {
|
||
if (typeof filterMethod === 'function' && filterMethod(node, val)) {
|
||
// 命中
|
||
return true;
|
||
}
|
||
}
|
||
return false;
|
||
});
|
||
self._setSuggestionMenu(filterNodes);
|
||
}
|
||
}, debounce);
|
||
}
|
||
|
||
if (multiple) {
|
||
// 多选可搜索
|
||
this.$tagsInput = $('<input type="text" autocomplete="off" placeholder="' + placeholder + '" class="el-cascader__search-input">');
|
||
var $tagsInput = this.$tagsInput;
|
||
this.$tags.append($tagsInput);
|
||
$tagsInput.on('keydown', filter);
|
||
$tagsInput.click(function (event) {
|
||
if (self.isFiltering) {
|
||
stopPropagation(event);
|
||
}
|
||
});
|
||
} else {
|
||
var $inputRow = this.$inputRow;
|
||
// 单选可搜索
|
||
$inputRow.removeAttr('readonly');
|
||
$inputRow.on('keydown', filter);
|
||
$inputRow.click(function (event) {
|
||
if (self.isFiltering) {
|
||
stopPropagation(event);
|
||
}
|
||
});
|
||
}
|
||
},
|
||
// 初始化面板(panel(1))
|
||
_initPanel: function () {
|
||
var $panel = this.$panel;
|
||
var popperClass = this.config.popperClass || '';
|
||
if (!$panel) {
|
||
// z-index:解决和layer.open默认19891016的冲突
|
||
this.$panel = $('<div class="el-popper el-cascader__dropdown ' + popperClass + '" style="position: absolute; z-index: 109891015;display: none;" x-placement="bottom-start"><div class="el-cascader-panel"></div><div class="popper__arrow" style="left: 35px;"></div></div>');
|
||
$panel = this.$panel;
|
||
$panel.appendTo('body');
|
||
$panel.click(function (event) {
|
||
// 阻止事件冒泡
|
||
stopPropagation(event);
|
||
});
|
||
// 初始化可搜索面板
|
||
this._initSuggestionPanel();
|
||
}
|
||
},
|
||
/**
|
||
* 添加菜单(panel(1)->menu(n))
|
||
* @param nodes 当前层级数据
|
||
* @param level 层级,从0开始
|
||
* @param parentNode 父级节点
|
||
* @param _menuItem 刷新时,传入的当前菜单的item数据
|
||
* @private
|
||
*/
|
||
_appendMenu: function (nodes, level, parentNode, _menuItem) {
|
||
this._removeMenu(level);
|
||
|
||
if (parentNode && parentNode.leaf) {
|
||
return;
|
||
}
|
||
|
||
var menuData = this.data.menuData;
|
||
var $div = $('<div class="el-scrollbar el-cascader-menu" role="menu" id="cascader-menu"><div class="el-cascader-menu__wrap el-scrollbar__wrap" style="margin-bottom: -17px; margin-right: -17px;"><ul class="el-scrollbar__view el-cascader-menu__list"></ul></div></div>');
|
||
// 重新添加菜单
|
||
this.$panel.find('.el-cascader-panel').append($div);
|
||
// 渲染细项
|
||
this._appendLi($div, nodes);
|
||
var menuItem = {nodes: nodes, level: level, parentNode: parentNode, scrollbar: {top: 0, left: 0}};
|
||
if (_menuItem) {
|
||
menuItem.scrollbar = _menuItem.scrollbar
|
||
}
|
||
// 渲染滚动条
|
||
this._initScrollbar($div, menuItem);
|
||
// 重新定位面板
|
||
this._resetXY();
|
||
menuData.push(menuItem);
|
||
},
|
||
/**
|
||
* 移除菜单
|
||
* @param level
|
||
* @private
|
||
*/
|
||
_removeMenu: function (level) {
|
||
// 除了上一层的所有菜单全部移除
|
||
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();
|
||
}
|
||
// 保存菜单数据
|
||
var menuData = this.data.menuData;
|
||
if (menuData.length > level) {
|
||
menuData.splice(level, menuData.length - level);
|
||
}
|
||
},
|
||
/**
|
||
* 添加细项(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('<div class="el-cascader-menu__empty-text">' + isEmpty + '</div>');
|
||
return;
|
||
}
|
||
$.each(nodes, function (index, node) {
|
||
node.bind($list);
|
||
});
|
||
},
|
||
/**
|
||
* 刷新菜单面板
|
||
*/
|
||
refreshMenu: function () {
|
||
// 先复制一个数组,避免刷新菜单时,数组的数据被改变
|
||
var data = this.data.menuData.concat([]);
|
||
var self = this;
|
||
data.forEach(function (data) {
|
||
self._appendMenu(data.nodes, data.level, data.parentNode, data);
|
||
})
|
||
},
|
||
/**
|
||
* 初始化可搜索面板
|
||
* @private
|
||
*/
|
||
_initSuggestionPanel: function () {
|
||
var filterable = this.config.filterable;
|
||
if (!filterable) {
|
||
return;
|
||
}
|
||
var $suggestionPanel = this.$suggestionPanel;
|
||
if (!$suggestionPanel) {
|
||
this.$suggestionPanel = $('<div class="el-cascader__suggestion-panel el-scrollbar" style="display: none;"><div class="el-scrollbar__wrap" style="margin-bottom: -17px; margin-right: -17px;"><ul class="el-scrollbar__view el-cascader__suggestion-list" style="min-width: 222px;"></ul></div></div>');
|
||
$suggestionPanel = this.$suggestionPanel;
|
||
this.$panel.find('.popper__arrow').before($suggestionPanel);
|
||
$suggestionPanel.click(function (event) {
|
||
// 阻止事件冒泡
|
||
stopPropagation(event);
|
||
});
|
||
}
|
||
},
|
||
/**
|
||
* 设置可搜索菜单
|
||
* @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('<li class="el-cascader__empty-text">无匹配数据</li>');
|
||
return;
|
||
}
|
||
$.each(nodes, function (index, node) {
|
||
node.bindSuggestion($list);
|
||
});
|
||
this._initScrollbar($suggestionPanel, {scrollbar: {top: 0, left: 0}});
|
||
this._resetXY();
|
||
},
|
||
/**
|
||
* 初始化节点数据
|
||
* @param data 原始数据
|
||
* @param level 层级
|
||
* @param parentNode 父级节点
|
||
* @returns {*[]}
|
||
*/
|
||
initNodes: function (data, level, parentNode) {
|
||
var nodes = [];
|
||
for (var key in data) {
|
||
var datum = data[key];
|
||
var node = new Node(datum, this, level, parentNode);
|
||
// 过滤value为null或者undefined的数据
|
||
if (node.value === null || node.value === undefined) {
|
||
continue;
|
||
}
|
||
nodes.push(node);
|
||
if (node.children && node.children.length > 0) {
|
||
node.setChildren(this.initNodes(node.children, level + 1, node));
|
||
}
|
||
}
|
||
return nodes;
|
||
},
|
||
/**
|
||
* 设置单选值
|
||
* @param nodeId 节点id
|
||
* @param node 节点对象
|
||
* @private
|
||
*/
|
||
_setActiveValue: function (nodeId, node) {
|
||
if (this.data.activeNodeId !== nodeId) {
|
||
var activeNode = this.data.activeNode;
|
||
this.data.activeNodeId = nodeId;
|
||
this.data.activeNode = node;
|
||
activeNode && activeNode.transferParent(function (node) {
|
||
node.syncStyle();
|
||
}, true);
|
||
node && node.transferParent(function (node) {
|
||
node.syncStyle();
|
||
}, true);
|
||
// 填充路径
|
||
this.change(node && node.value, node);
|
||
if (nodeId !== null) {
|
||
this._setClear();
|
||
}
|
||
}
|
||
},
|
||
/**
|
||
* 设置多选值
|
||
* @param nodeIds 值数组
|
||
* @param nodes 节点数组
|
||
* @private
|
||
*/
|
||
_setCheckedValue: function (nodeIds, nodes) {
|
||
var checkedNodes = this.data.checkedNodes;
|
||
var maxSize = this.config.maxSize;
|
||
var maxSizeMode;
|
||
if (nodeIds.length > 0 && maxSize !== 0 && nodeIds.length >= maxSize) {
|
||
nodeIds = nodeIds.slice(0, maxSize);
|
||
nodes = nodes.slice(0, maxSize);
|
||
maxSizeMode = true
|
||
} else {
|
||
maxSizeMode = false
|
||
}
|
||
this.data.checkedNodeIds = nodeIds || [];
|
||
this.data.checkedNodes = nodes || [];
|
||
var syncPath = [];
|
||
var syncNodeIds = [];
|
||
checkedNodes.forEach(function (node) {
|
||
node.path.forEach(function (node) {
|
||
if (syncNodeIds.indexOf(node.nodeId) === -1) {
|
||
syncPath.push(node);
|
||
syncNodeIds.push(node.nodeId);
|
||
}
|
||
});
|
||
});
|
||
nodes.forEach(function (node) {
|
||
node.path.forEach(function (node) {
|
||
if (syncNodeIds.indexOf(node.nodeId) === -1) {
|
||
syncPath.push(node);
|
||
syncNodeIds.push(node.nodeId);
|
||
}
|
||
});
|
||
});
|
||
syncPath.forEach(function (node) {
|
||
node.syncStyle();
|
||
});
|
||
// 填充路径
|
||
this.change(nodes.map(function (node) {
|
||
return node.value;
|
||
}), nodes);
|
||
this._setClear();
|
||
this.maxSizeMode = maxSizeMode;
|
||
},
|
||
/**
|
||
* 设置值
|
||
* @param value
|
||
*/
|
||
setValue: function (value) {
|
||
if (this.data.activeNodeId || this.data.checkedNodeIds.length > 0) {
|
||
// 清空值
|
||
this.clearCheckedNodes();
|
||
}
|
||
if (!value) {
|
||
return;
|
||
}
|
||
var strictMode = this.props.strictMode;
|
||
if (strictMode) {
|
||
if (!Array.isArray(value)) {
|
||
throw new Error("严格模式下,value必须是一个包含父子节点数组结构.");
|
||
}
|
||
}
|
||
var nodes = this.getNodes(this.data.nodes);
|
||
var checkStrictly = this.props.checkStrictly;
|
||
var multiple = this.props.multiple;
|
||
var disabledFixed = this.config.disabledFixed;
|
||
if (multiple) {
|
||
var paths = nodes.filter(function (node) {
|
||
if ((checkStrictly || node.leaf) && (!node.disabled || disabledFixed)) {
|
||
if (strictMode) {
|
||
// 严格模式下
|
||
// some:命中一个就为true
|
||
// every:全部命中为true
|
||
return value.some(function (levelValue) {
|
||
if (!Array.isArray(levelValue)) {
|
||
throw new Error("多选严格模式下,value必须是一个二维数组结构.");
|
||
}
|
||
var path = node.path;
|
||
return levelValue.length === path.length && levelValue.every(function (rowValue, index) {
|
||
return path[index].value === rowValue;
|
||
});
|
||
})
|
||
} else {
|
||
return value.indexOf(node.value) !== -1;
|
||
}
|
||
}
|
||
return false;
|
||
});
|
||
var nodeIds = paths.map(function (node) {
|
||
return node.nodeId;
|
||
});
|
||
this._setCheckedValue(nodeIds, paths);
|
||
// 展开第一个节点
|
||
if (paths.length > 0) {
|
||
var first = paths[0];
|
||
first.expandPanel();
|
||
}
|
||
} else {
|
||
for (var i = 0; i < nodes.length; i++) {
|
||
var node = nodes[i];
|
||
if ((checkStrictly || node.leaf)) {
|
||
var is = false;
|
||
if (strictMode) {
|
||
// 严格模式下
|
||
// every:全部命中为true
|
||
var path = node.path;
|
||
is = value.length === path.length && value.every(function (rowValue, index) {
|
||
return path[index].value === rowValue;
|
||
});
|
||
} else if (node.value === value) {
|
||
is = true;
|
||
}
|
||
if (is) {
|
||
this._setActiveValue(node.nodeId, node);
|
||
// 展开节点
|
||
node.expandPanel();
|
||
break;
|
||
}
|
||
}
|
||
}
|
||
}
|
||
},
|
||
/**
|
||
* 递归获取扁平的节点
|
||
* @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;
|
||
},
|
||
/**
|
||
* 初始化滚动条
|
||
* @param $menu 菜单的dom节点对象
|
||
* @param menuItem 当前菜单数据
|
||
* @private
|
||
*/
|
||
_initScrollbar: function ($menu, menuItem) {
|
||
var $div = $('<div class="el-scrollbar__bar is-onhoriztal"><div class="el-scrollbar__thumb" style="transform: translateX(0%);"></div></div><div class="el-scrollbar__bar is-vertical"><div class="el-scrollbar__thumb" style="transform: translateY(0%);"></div></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(scrollTop, scrollLeft) {
|
||
if (hScale < 1) {
|
||
vertical.css('height', hScale * 100 + '%');
|
||
vertical.css('transform', 'translateY(' + scrollTop / $menu.height() * 100 + '%)');
|
||
}
|
||
if (wScale < 1) {
|
||
onhoriztal.css('width', wScale * 100 + '%');
|
||
onhoriztal.css('transform', 'translateY(' + scrollLeft / $menu.width() * 100 + '%)');
|
||
}
|
||
}
|
||
|
||
// 拖动事件
|
||
vertical.mousedown(function (event) {
|
||
event.stopImmediatePropagation();
|
||
stopPropagation(event);
|
||
// 禁止文本选择事件
|
||
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) {
|
||
stopPropagation(event);
|
||
event.stopImmediatePropagation();
|
||
$(document).off('mousemove', mousemove);
|
||
$(document).off('selectstart', selectstart);
|
||
});
|
||
});
|
||
// 监听滚动条事件
|
||
scrollbar.scroll(function () {
|
||
var scroll = $(this);
|
||
menuItem.scrollbar.top = scroll.scrollTop()
|
||
menuItem.scrollbar.left = scroll.scrollLeft()
|
||
_scrollbarEvent(menuItem.scrollbar.top, menuItem.scrollbar.left);
|
||
});
|
||
|
||
// 初始化滚动条
|
||
scrollbar.scrollTop(menuItem.scrollbar.top);
|
||
_scrollbarEvent(menuItem.scrollbar.top, menuItem.scrollbar.left);
|
||
},
|
||
// 填充路径
|
||
_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 activeNode = this.data.activeNode;
|
||
var path = activeNode && activeNode.path || [];
|
||
if (showAllLevels) {
|
||
this._$inputRowSetValue(path.map(function (node) {
|
||
return node.label;
|
||
}).join(separator));
|
||
} else {
|
||
this._$inputRowSetValue(activeNode && activeNode.label || "");
|
||
}
|
||
} else {
|
||
// 复选框
|
||
|
||
// 删除标签
|
||
this.$tags.find('.el-tag').remove();
|
||
var $tagsInput = this.$tagsInput;
|
||
// 清除高度
|
||
$inputRow.css('height', '');
|
||
var checkedNodes = this.data.checkedNodes;
|
||
var minCollapseTagsNumber = Math.max(this.config.minCollapseTagsNumber, 1);
|
||
if (checkedNodes.length > 0) {
|
||
var tags = [];
|
||
var paths = checkedNodes;
|
||
if (collapseTags) {
|
||
// 折叠tags
|
||
paths = checkedNodes.slice(0, Math.min(checkedNodes.length, minCollapseTagsNumber));
|
||
}
|
||
paths.forEach(function (node) {
|
||
tags.push(node.$tag);
|
||
});
|
||
// 判断标签是否折叠
|
||
if (collapseTags) {
|
||
// 判断标签最小折叠数
|
||
if (checkedNodes.length > minCollapseTagsNumber) {
|
||
tags.push(self.get$tag('+ ' + (checkedNodes.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 (checkedNodes.length > 0) {
|
||
$inputRow.removeAttr('placeholder');
|
||
$tagsInput && $tagsInput.removeAttr('placeholder', placeholder);
|
||
} else {
|
||
$inputRow.attr('placeholder', placeholder);
|
||
$tagsInput && $tagsInput.attr('placeholder', placeholder);
|
||
}
|
||
}
|
||
},
|
||
/**
|
||
* 设置单选输入框的值
|
||
* @param label
|
||
* @private
|
||
*/
|
||
_$inputRowSetValue: function (label) {
|
||
label = label || "";
|
||
var $inputRow = this.$inputRow;
|
||
$inputRow.attr('value', label); //防止被重置
|
||
$inputRow.val(label);
|
||
},
|
||
/**
|
||
* 获取复选框标签对象
|
||
* @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 ? '<i class="el-tag__close el-icon-close ' + fromIcon + ' ' + closeIcon + '"></i>' : '';
|
||
return $('<span class="el-tag el-tag--info el-tag--small el-tag--light"><span>' + label + '</span>' + icon + '</span>');
|
||
},
|
||
// 设置可清理
|
||
_setClear: function () {
|
||
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.off('click');
|
||
var multiple = this.props.multiple;
|
||
var clear;
|
||
if (multiple) {
|
||
clear = this.data.checkedNodeIds.length > 0;
|
||
} else {
|
||
clear = !!this.data.activeNodeId;
|
||
}
|
||
if (clear && !this.config.disabled && this.config.clearable) {
|
||
self.$icon.one('click', function (event) {
|
||
stopPropagation(event);
|
||
self.close();
|
||
self.clearCheckedNodes();
|
||
out();
|
||
self.$icon.off('mouseenter');
|
||
self.$div.off('mouseenter');
|
||
self.$div.off('mouseleave');
|
||
});
|
||
} else {
|
||
out();
|
||
self.$icon.off('mouseenter');
|
||
self.$div.off('mouseenter');
|
||
self.$div.off('mouseleave');
|
||
}
|
||
},
|
||
// 禁用
|
||
disabled: function (isDisabled) {
|
||
this.config.disabled = !!isDisabled;
|
||
if (this.config.disabled) {
|
||
this.$div.addClass('is-disabled');
|
||
this.$div.find('.el-input--suffix').addClass('is-disabled');
|
||
this.$inputRow.attr('disabled', 'disabled');
|
||
this.$tagsInput && this.$tagsInput.attr('disabled', 'disabled').hide()
|
||
} else {
|
||
this.$div.removeClass('is-disabled');
|
||
this.$div.find('.el-input--suffix').removeClass('is-disabled');
|
||
this.$inputRow.removeAttr('disabled');
|
||
this.$tagsInput && this.$tagsInput.removeAttr('disabled').show();
|
||
}
|
||
// 重新设置是否可被清理
|
||
this._setClear();
|
||
// 重新填充路径
|
||
this._fillingPath();
|
||
},
|
||
/**
|
||
* 当选中节点变化时触发 选中节点的值
|
||
* @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.event.change.forEach(function (e) {
|
||
typeof e === 'function' && e(value, node)
|
||
})
|
||
},
|
||
/**
|
||
* 当失去焦点时触发 (event: Event)
|
||
* @param eventId 不为空时,必须与closeEventId值相等,防止旧事件触发
|
||
*/
|
||
close: function (eventId) {
|
||
if (this.showPanel && (!eventId || this.closeEventId === eventId)) {
|
||
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();
|
||
}
|
||
this.event.close.forEach(function (e) {
|
||
typeof e === 'function' && e()
|
||
})
|
||
}
|
||
},
|
||
/**
|
||
* 当获得焦点时触发 (event: Event)
|
||
*/
|
||
open: function () {
|
||
if (!this.showPanel) {
|
||
this.showPanel = true;
|
||
this.closeEventId++;
|
||
var self = this;
|
||
// 当前传播事件结束后,添加点击背景关闭面板事件
|
||
setTimeout(function () {
|
||
$(document).one('click', self.close.bind(self, self.closeEventId));
|
||
});
|
||
// 重新定位面板
|
||
this._resetXY();
|
||
// 箭头icon翻转
|
||
this.$div.find('.layui-icon-down').addClass('is-reverse');
|
||
this.$panel.slideDown(200);
|
||
this.visibleChange(true);
|
||
// 聚焦颜色
|
||
this.$input.addClass('is-focus');
|
||
this.event.open.forEach(function (e) {
|
||
typeof e === 'function' && e()
|
||
})
|
||
}
|
||
},
|
||
/**
|
||
* 下拉框出现/隐藏时触发
|
||
* @param visible 出现则为 true,隐藏则为 false
|
||
*/
|
||
visibleChange: function (visible) {
|
||
},
|
||
/**
|
||
* 在多选模式下,移除Tag时触发 移除的Tag对应的节点的值
|
||
* @param tagValue 节点的值
|
||
* @param node 节点对象
|
||
*/
|
||
removeTag: function (tagValue, node) {
|
||
},
|
||
/**
|
||
* 获取选中的节点值
|
||
* @returns {null|[]}
|
||
*/
|
||
getCheckedValues: function () {
|
||
var strictMode = this.props.strictMode;
|
||
if (this.props.multiple) {
|
||
var checkedNodes = this.data.checkedNodes;
|
||
if (strictMode) {
|
||
return checkedNodes.map(function (node) {
|
||
return node.path.map(function (node1) {
|
||
return node1.value;
|
||
});
|
||
});
|
||
}
|
||
return checkedNodes.map(function (node) {
|
||
return node.value;
|
||
});
|
||
} else {
|
||
var activeNode = this.data.activeNode;
|
||
if (strictMode) {
|
||
return activeNode && activeNode.path.map(function (node) {
|
||
return node.value;
|
||
})
|
||
}
|
||
return activeNode && activeNode.value;
|
||
}
|
||
},
|
||
/**
|
||
* 获取选中的节点
|
||
* @returns {null|[]}
|
||
*/
|
||
getCheckedNodes: function () {
|
||
var strictMode = this.props.strictMode;
|
||
if (this.props.multiple) {
|
||
var checkedNodes = this.data.checkedNodes;
|
||
if (strictMode) {
|
||
return checkedNodes && checkedNodes.map(function (node) {
|
||
return node.path;
|
||
});
|
||
}
|
||
return checkedNodes;
|
||
} else {
|
||
var activeNode = this.data.activeNode;
|
||
if (strictMode) {
|
||
return activeNode && activeNode.path;
|
||
}
|
||
return activeNode;
|
||
}
|
||
},
|
||
/**
|
||
* 清空选中的节点
|
||
* @param force 强制清理禁用固定节点
|
||
*/
|
||
clearCheckedNodes: function (force) {
|
||
var multiple = this.props.multiple;
|
||
if (multiple) {
|
||
var disabledFixed = this.config.disabledFixed;
|
||
if (!force && disabledFixed) {
|
||
//禁用项被固定,则过滤出禁用项的选值出来
|
||
var checkedNodes = this.data.checkedNodes;
|
||
var disNodes = checkedNodes.filter(function (node) {
|
||
return node.disabled;
|
||
});
|
||
var nodeIds = disNodes.map(function (node) {
|
||
return node.nodeId;
|
||
});
|
||
this._setCheckedValue(nodeIds, disNodes);
|
||
} else {
|
||
this._setCheckedValue([], []);
|
||
}
|
||
} else {
|
||
this._setActiveValue(null, null);
|
||
}
|
||
}
|
||
};
|
||
|
||
var thisCas = function () {
|
||
var self = this;
|
||
return {
|
||
/**
|
||
* 设置选项值
|
||
* @param options
|
||
*/
|
||
setOptions: function (options) {
|
||
self.setOptions(options);
|
||
},
|
||
/**
|
||
* 覆盖当前值
|
||
* @param value 单选时传对象,多选时传数组
|
||
*/
|
||
setValue: function (value) {
|
||
self.setValue(value);
|
||
},
|
||
/**
|
||
* 当节点变更时,执行回调
|
||
* @param callback function(value,node){}
|
||
*/
|
||
changeEvent: function (callback) {
|
||
self.event.change.push(callback);
|
||
},
|
||
/**
|
||
* 当面板关闭时,执行回调
|
||
* @param callback function(){}
|
||
*/
|
||
closeEvent: function (callback) {
|
||
self.event.close.push(callback);
|
||
},
|
||
/**
|
||
* 当面板打开时,执行回调
|
||
* @param callback function(){}
|
||
*/
|
||
openEvent: function (callback) {
|
||
self.event.open.push(callback);
|
||
},
|
||
/**
|
||
* 禁用组件
|
||
* @param disabled true/false
|
||
*/
|
||
disabled: function (disabled) {
|
||
self.disabled(disabled);
|
||
},
|
||
/**
|
||
* 收起面板
|
||
*/
|
||
close: function () {
|
||
self.close();
|
||
},
|
||
/**
|
||
* 展开面板
|
||
*/
|
||
open: function () {
|
||
self.open();
|
||
},
|
||
/**
|
||
* 获取选中的节点,如需获取路径,使用node.path获取,将获取各级节点的node对象
|
||
* @returns {[]|*}
|
||
*/
|
||
getCheckedNodes: function () {
|
||
return self.getCheckedNodes();
|
||
},
|
||
/**
|
||
* 获取选中的值
|
||
* @returns {[]|*}
|
||
*/
|
||
getCheckedValues: function () {
|
||
return self.getCheckedValues();
|
||
},
|
||
/**
|
||
* 清空选中的节点
|
||
* @param force 强制清理禁用固定节点
|
||
*/
|
||
clearCheckedNodes: function (force) {
|
||
self.clearCheckedNodes(force);
|
||
},
|
||
/**
|
||
* 展开面板到节点所在的层级
|
||
* @param value 节点值,只能传单个值,不允许传数组
|
||
*/
|
||
expandNode: function (value) {
|
||
var nodes = self.getNodes(self.data.nodes);
|
||
for (var i = 0; i < nodes.length; i++) {
|
||
var node = nodes[i];
|
||
if (node.value === value) {
|
||
node.expandPanel();
|
||
break;
|
||
}
|
||
}
|
||
},
|
||
/**
|
||
* 获取当前配置副本
|
||
*/
|
||
getConfig: function () {
|
||
return $.extend(true, {}, self.config);
|
||
},
|
||
/**
|
||
* 获取数据对象副本
|
||
* @returns {*}
|
||
*/
|
||
getData: function () {
|
||
return $.extend(true, {}, self.data);
|
||
}
|
||
};
|
||
};
|
||
|
||
exports('layCascader', function (option) {
|
||
var ins = new Cascader(option);
|
||
return thisCas.call(ins);
|
||
});
|
||
});
|