123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085 |
- /**
- * 仿element-ui,级联选择器
- * 已实现单选、多选、无关联选择
- * 其他功能:组件禁用、节点禁用、自定义属性、自定义空面板提示,自定义无选择时的提示、多选标签折叠、回显、搜索、动态加载、最大选中数量限制、禁用项固定等操作。
- * element-ui没有的功能:最大选中数量限制、禁用项固定
- * author: yixiaco
- * gitee: https://gitee.com/yixiacoco/lay_cascader
- * github: https://github.com/yixiaco/lay_cascader
- */
- layui.define(["jquery"], function (exports) {
- var $ = layui.jquery;
- /**
- * 级联各项节点对象
- * @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) {
- event.stopPropagation();
- 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) {
- event.stopPropagation();
- 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) {
- event.stopPropagation();
- 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) {
- event.stopPropagation();
- 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) {
- event.stopPropagation();
- 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) {
- event.stopPropagation();
- 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 = false;
- }
- 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);
- }
- this._initHideElement($e);
- // 替换元素
- $e.replaceWith(this.$div);
- this.$icon = this.$input.find('i');
- this._initFilterableInputEvent();
- this.disabled(this.config.disabled);
- },
- /**
- * 初始化隐藏元素input,主要用于layui的表单验证
- * @param $e
- * @private
- */
- _initHideElement: function ($e) {
- // 保存原始元素
- 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);
- },
- /**
- * 初始化可搜索监听事件
- * @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) {
- 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;
- 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) {
- // 阻止事件冒泡
- event.stopPropagation();
- });
- // 初始化可搜索面板
- 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) {
- // 阻止事件冒泡
- 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, {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);
- 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();
- 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 () {
- 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) {
- event.stopPropagation();
- 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);
- });
- });
|