Files
OMS/app/desktop/statics/js/cascader.js
2025-12-28 23:13:25 +08:00

1597 lines
50 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/**
* 仿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 = $('<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();
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 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 = '<i class="' + fromIcon + ' ' + okIcon + ' el-icon-check"></i>';
$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('<i class="' + fromIcon + ' ' + okIcon + ' el-cascader-node__prefix"></i>');
}
$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('<i class="' + fromIcon + ' ' + okIcon + ' el-cascader-node__prefix"></i>');
}
}
},
/**
* 单选&&非关联
* @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 = $('<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="' + value + '"></span><span class="el-radio__label"><span></span></span></label>');
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 = $('<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 (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 = $('<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 = 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 = $('<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);
}
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 = $('<input type="text" 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) {
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 = $('<div class="el-popper el-cascader__dropdown" style="position: absolute; z-index: 19891015;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');
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 = $('<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>');
// 除了上一层的所有菜单全部移除
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('<div class="el-cascader-menu__empty-text">' + isEmpty + '</div>');
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 = $('<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) {
// 阻止事件冒泡
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('<li class="el-cascader__empty-text">无匹配数据</li>');
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 = $('<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($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 ? '<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 () {
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);
});
});