layCascader.js 80 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085
  1. /**
  2. * 仿element-ui,级联选择器
  3. * 已实现单选、多选、无关联选择
  4. * 其他功能:组件禁用、节点禁用、自定义属性、自定义空面板提示,自定义无选择时的提示、多选标签折叠、回显、搜索、动态加载、最大选中数量限制、禁用项固定等操作。
  5. * element-ui没有的功能:最大选中数量限制、禁用项固定
  6. * author: yixiaco
  7. * gitee: https://gitee.com/yixiacoco/lay_cascader
  8. * github: https://github.com/yixiaco/lay_cascader
  9. */
  10. layui.define(["jquery"], function (exports) {
  11. var $ = layui.jquery;
  12. /**
  13. * 级联各项节点对象
  14. * @param data 原始对象信息
  15. * @param cascader 级联对象
  16. * @param level 层级,从0开始
  17. * @param parentNode 父节点对象
  18. * @constructor
  19. */
  20. function Node(data, cascader, level, parentNode) {
  21. this.data = data;
  22. this.cascader = cascader;
  23. this.config = cascader.config;
  24. this.props = cascader.props;
  25. this.level = level;
  26. this.parentNode = parentNode;
  27. // 引入的icon图标
  28. this.icons = cascader.icons;
  29. //该节点是否被选中 0:未选中,1:选中,2:不定
  30. this._checked = 0;
  31. // 是否正在加载中
  32. this._loading = false;
  33. // 每个Node的唯一标识
  34. this.nodeId = cascader.data.nodeId++;
  35. }
  36. Node.prototype = {
  37. constructor: Node,
  38. /** 最顶级父节点 */
  39. get topParentNode() {
  40. return !this.parentNode && this || this.topParentNode;
  41. },
  42. /** 子节点 */
  43. childrenNode: undefined,
  44. get loading() {
  45. return this._loading;
  46. },
  47. set loading(loading) {
  48. var $li = this.$li;
  49. if ($li) {
  50. var rightIcon = this.icons.right;
  51. var loadingIcon = this.icons.loading;
  52. var $i = $li.find('i');
  53. if (loading) {
  54. $i.addClass(loadingIcon);
  55. $i.removeClass(rightIcon);
  56. } else {
  57. $i.addClass(rightIcon);
  58. $i.removeClass(loadingIcon);
  59. }
  60. }
  61. return this._loading = loading;
  62. },
  63. /** 当前节点的显示文本 */
  64. get label() {
  65. return this.data[this.props.label];
  66. },
  67. /** 当前节点的值 */
  68. get value() {
  69. return this.data[this.props.value];
  70. },
  71. /** 是否禁用 */
  72. get disabled() {
  73. var multiple = this.props.multiple;
  74. var maxSize = this.config.maxSize;
  75. var checkedNodeIds = this.cascader.data.checkedNodeIds;
  76. var disabledName = this.props.disabled;
  77. var checkStrictly = this.props.checkStrictly;
  78. // 检查是否超过最大值限制
  79. if (multiple && maxSize !== 0) {
  80. if (checkedNodeIds.length >= maxSize && checkedNodeIds.indexOf(this.nodeId) === -1) {
  81. // 如果是关联的多选,需要查询叶子节点是否有被选中的项
  82. if (!checkStrictly) {
  83. var leafChildren = this.getAllLeafChildren();
  84. var nodeIds = leafChildren.map(function (value) {
  85. return value.nodeId
  86. });
  87. // 如果叶子节点不包含,则直接返回true
  88. if (!nodeIds.some(function (nodeId) {
  89. return checkedNodeIds.indexOf(nodeId) !== -1;
  90. })) {
  91. return true;
  92. }
  93. } else {
  94. return true;
  95. }
  96. }
  97. }
  98. if (!checkStrictly) {
  99. var path = this.path;
  100. return path.some(function (node) {
  101. return node.data[disabledName];
  102. });
  103. } else {
  104. return this.data[disabledName];
  105. }
  106. },
  107. /** 子节点数据 */
  108. get children() {
  109. return this.data[this.props.children];
  110. },
  111. set children(children) {
  112. this.data[this.props.children] = children;
  113. },
  114. /** 叶子节点 */
  115. get leaf() {
  116. var leaf = this.data[this.props.leaf];
  117. if (typeof leaf === 'boolean') {
  118. return leaf;
  119. }
  120. // 如果children不为空,则判断是否是子节点
  121. if (this.children) {
  122. return this.children.length <= 0;
  123. }
  124. return true;
  125. },
  126. /** 当前单选值 */
  127. get activeNodeId() {
  128. return this.cascader.data.activeNodeId;
  129. },
  130. /** 当前复选框值 */
  131. get checkedNodeIds() {
  132. return this.cascader.data.checkedNodeIds;
  133. },
  134. /** 路径 */
  135. get path() {
  136. var parentNode = this.parentNode;
  137. if (parentNode) {
  138. return parentNode.path.concat([this]);
  139. } else {
  140. return [this];
  141. }
  142. },
  143. /** 是否正在搜索中 */
  144. get isFiltering() {
  145. return this.cascader.isFiltering;
  146. },
  147. /** 输入框的tag标签 */
  148. get $tag() {
  149. var cascader = this.cascader;
  150. var showAllLevels = this.config.showAllLevels;
  151. var disabled = this.config.disabled;
  152. var nodeDisabled = this.disabled;
  153. var disabledFixed = this.config.disabledFixed;
  154. var label = this.getPathLabel(showAllLevels);
  155. var $tag = cascader.get$tag(label, !disabled && (!nodeDisabled || !disabledFixed));
  156. var self = this;
  157. $tag.find('i').click(function (event) {
  158. event.stopPropagation();
  159. self.selectedValue();
  160. cascader.removeTag(self.value, self);
  161. });
  162. return $tag;
  163. },
  164. /**
  165. * 完整路径的标签
  166. * @param showAllLevels
  167. * @returns {string}
  168. */
  169. getPathLabel: function (showAllLevels) {
  170. var path = this.path;
  171. var separator = this.config.separator;
  172. var label;
  173. if (showAllLevels) {
  174. label = path.map(function (node) {
  175. return node.label;
  176. }).join(separator);
  177. } else {
  178. label = path[path.length - 1].label;
  179. }
  180. return label;
  181. },
  182. /**
  183. * 初始化
  184. */
  185. init: function () {
  186. var multiple = this.props.multiple;
  187. var checkStrictly = this.props.checkStrictly;
  188. var fromIcon = this.icons.from;
  189. var rightIcon = this.icons.right;
  190. var icon = '';
  191. var label = this.label;
  192. if (!this.leaf) {
  193. icon = rightIcon;
  194. }
  195. 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>');
  196. // 节点渲染
  197. if (!multiple && !checkStrictly) {
  198. this._renderRadio();
  199. } else if (!multiple && checkStrictly) {
  200. this._renderRadioCheckStrictly();
  201. } else if (multiple && !checkStrictly) {
  202. this._renderMultiple();
  203. } else if (multiple && checkStrictly) {
  204. this._renderMultipleCheckStrictly();
  205. }
  206. },
  207. /**
  208. * 初始化可搜索li
  209. */
  210. initSuggestionLi: function () {
  211. var label = this.getPathLabel(true);
  212. this.$suggestionLi = $('<li tabindex="-1" class="el-cascader__suggestion-item"><span>' + label + '</span></li>');
  213. // 节点渲染
  214. this._renderFiltering();
  215. },
  216. /**
  217. * 绑定到菜单中
  218. * @param $list li节点
  219. */
  220. bind: function ($list) {
  221. this.init();
  222. $list.append(this.$li);
  223. },
  224. /**
  225. * 绑定可搜索到列表中
  226. * @param $list
  227. */
  228. bindSuggestion: function ($list) {
  229. this.initSuggestionLi();
  230. $list.append(this.$suggestionLi);
  231. },
  232. /**
  233. * 可搜索渲染
  234. * @private
  235. */
  236. _renderFiltering: function () {
  237. var $li = this.$suggestionLi;
  238. var nodeId = this.nodeId;
  239. var fromIcon = this.icons.from;
  240. var okIcon = this.icons.ok;
  241. var self = this;
  242. var cascader = this.cascader;
  243. var multiple = this.props.multiple;
  244. var icon = '<i class="' + fromIcon + ' ' + okIcon + ' el-icon-check"></i>';
  245. $li.click(function (event) {
  246. event.stopPropagation();
  247. self.selectedValue();
  248. if (multiple) {
  249. if (self.checkedNodeIds.indexOf(nodeId) === -1) {
  250. $li.removeClass('is-checked');
  251. $li.find('.el-icon-check').remove();
  252. } else {
  253. $li.addClass('is-checked');
  254. $li.append(icon);
  255. }
  256. } else {
  257. // 关闭面板
  258. cascader.close();
  259. }
  260. });
  261. if (multiple && self.checkedNodeIds.indexOf(nodeId) !== -1
  262. || !multiple && self.activeNodeId === nodeId) {
  263. $li.addClass('is-checked');
  264. $li.append(icon)
  265. }
  266. },
  267. /**
  268. * 单选&&关联
  269. * @private
  270. */
  271. _renderRadio: function () {
  272. var $li = this.$li;
  273. var nodeId = this.nodeId;
  274. var fromIcon = this.icons.from;
  275. var okIcon = this.icons.ok;
  276. var level = this.level;
  277. var leaf = this.leaf;
  278. var self = this;
  279. var cascader = this.cascader;
  280. var activeNode = this.cascader.data.activeNode;
  281. var parentNode = this.parentNode;
  282. if (self.activeNodeId && activeNode.path.some(function (node) {
  283. return node.nodeId === nodeId;
  284. })) {
  285. if (self.activeNodeId === nodeId) {
  286. $li.prepend('<i class="' + fromIcon + ' ' + okIcon + ' el-cascader-node__prefix"></i>');
  287. }
  288. $li.addClass('is-active');
  289. $li.addClass('in-checked-path');
  290. }
  291. // 是否禁用
  292. if (this.disabled) {
  293. $li.addClass('is-disabled');
  294. return;
  295. }
  296. $li.addClass('is-selectable');
  297. if (parentNode) {
  298. parentNode.$li.siblings().removeClass('in-active-path');
  299. parentNode.$li.addClass('in-active-path');
  300. }
  301. // 触发下一个节点
  302. this._liClick(function (event) {
  303. event.stopPropagation();
  304. var childrenNode = self.childrenNode;
  305. if (leaf && event.type === 'click') {
  306. self.selectedValue();
  307. // 关闭面板
  308. cascader.close();
  309. }
  310. // 添加下级菜单
  311. cascader._appendMenu(childrenNode, level + 1, self);
  312. });
  313. },
  314. /**
  315. * 单选&&非关联
  316. * @private
  317. */
  318. _renderRadioCheckStrictly: function () {
  319. var $li = this.$li;
  320. var nodeId = this.nodeId;
  321. var level = this.level;
  322. var leaf = this.leaf;
  323. var self = this;
  324. var cascader = this.cascader;
  325. var activeNode = cascader.data.activeNode;
  326. var parentNode = this.parentNode;
  327. $li.addClass('is-selectable');
  328. // 任意一级单选
  329. 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>');
  330. this.$radio = $radio;
  331. $li.prepend($radio);
  332. if (parentNode) {
  333. parentNode.$li.siblings().removeClass('in-active-path');
  334. parentNode.$li.addClass('in-active-path');
  335. }
  336. // 触发下一个节点
  337. this._liClick(function (event) {
  338. event.stopPropagation();
  339. var childrenNode = self.childrenNode;
  340. if (!self.disabled && leaf && event.type === 'click') {
  341. self.selectedValue();
  342. }
  343. // 添加下级菜单
  344. cascader._appendMenu(childrenNode, level + 1, self);
  345. });
  346. if (self.activeNodeId && activeNode.path.some(function (node) {
  347. return node.nodeId === nodeId;
  348. })) {
  349. if (self.activeNodeId === nodeId) {
  350. $radio.find('.el-radio__input').addClass('is-checked');
  351. }
  352. $li.addClass('is-active');
  353. $li.addClass('in-checked-path');
  354. }
  355. if (this.disabled) {
  356. $radio.addClass('is-disabled');
  357. $radio.find('.el-radio__input').addClass('is-disabled');
  358. return;
  359. }
  360. // 选中事件
  361. $radio.click(function (event) {
  362. event.preventDefault();
  363. !leaf && self.selectedValue();
  364. });
  365. },
  366. /**
  367. * 多选&&关联
  368. * @private
  369. */
  370. _renderMultiple: function () {
  371. var $li = this.$li;
  372. var level = this.level;
  373. var leaf = this.leaf;
  374. var self = this;
  375. var cascader = this.cascader;
  376. var checked = this._checked;
  377. var parentNode = this.parentNode;
  378. $li.addClass('el-cascader-node');
  379. // 多选框
  380. 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>');
  381. this.$checked = $checked;
  382. $li.prepend($checked);
  383. // 渲染
  384. if (checked === 1) {
  385. this.$checked.find('.el-checkbox__input').addClass('is-checked');
  386. } else if (checked === 2) {
  387. this.$checked.find('.el-checkbox__input').addClass('is-indeterminate');
  388. }
  389. if (parentNode) {
  390. parentNode.$li.siblings().removeClass('in-active-path');
  391. parentNode.$li.addClass('in-active-path');
  392. }
  393. // 触发下一个节点
  394. this._liClick(function (event) {
  395. event.stopPropagation();
  396. var childrenNode = self.childrenNode;
  397. if (!self.disabled && leaf && event.type === 'click') {
  398. // 最后一级就默认选择
  399. self.selectedValue();
  400. }
  401. // 添加下级菜单
  402. cascader._appendMenu(childrenNode, level + 1, self);
  403. });
  404. if (this.disabled) {
  405. $li.addClass('is-disabled');
  406. $checked.addClass('is-disabled');
  407. $checked.find('.el-checkbox__input').addClass('is-disabled');
  408. return;
  409. }
  410. // 选中事件
  411. $checked.click(function (event) {
  412. event.preventDefault();
  413. if (!leaf) {
  414. var childrenNode = self.childrenNode;
  415. self.selectedValue();
  416. cascader._appendMenu(childrenNode, level + 1, self);
  417. }
  418. });
  419. },
  420. /**
  421. * 多选&&非关联
  422. * @private
  423. */
  424. _renderMultipleCheckStrictly: function () {
  425. var $li = this.$li;
  426. var level = this.level;
  427. var leaf = this.leaf;
  428. var self = this;
  429. var cascader = this.cascader;
  430. var checkedNodeIds = cascader.data.checkedNodeIds;
  431. var checkedNodes = cascader.data.checkedNodes;
  432. var nodeId = this.nodeId;
  433. var parentNode = this.parentNode;
  434. $li.addClass('el-cascader-node is-selectable');
  435. // 多选框
  436. 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>');
  437. this.$checked = $checked;
  438. $li.prepend($checked);
  439. // 渲染
  440. var exist = checkedNodes.some(function (node) {
  441. return node.path.some(function (node) {
  442. return node.nodeId === nodeId;
  443. })
  444. });
  445. if (exist) {
  446. $li.addClass('in-checked-path');
  447. if (checkedNodeIds.indexOf(nodeId) !== -1) {
  448. this.$checked.find('.el-checkbox__input').addClass('is-checked');
  449. }
  450. }
  451. if (parentNode) {
  452. parentNode.$li.siblings().removeClass('in-active-path');
  453. parentNode.$li.addClass('in-active-path');
  454. }
  455. // 触发下一个节点
  456. this._liClick(function (event) {
  457. event.stopPropagation();
  458. var childrenNode = self.childrenNode;
  459. if (!self.disabled && leaf && event.type === 'click') {
  460. // 最后一级就默认选择
  461. self.selectedValue();
  462. }
  463. // 添加下级菜单
  464. cascader._appendMenu(childrenNode, level + 1, self);
  465. });
  466. if (this.disabled) {
  467. $checked.addClass('is-disabled');
  468. $checked.find('.el-checkbox__input').addClass('is-disabled');
  469. return;
  470. }
  471. // 选中事件
  472. $checked.click(function (event) {
  473. event.preventDefault();
  474. if (!leaf) {
  475. self.selectedValue();
  476. var childrenNode = self.childrenNode;
  477. // 添加下级菜单
  478. cascader._appendMenu(childrenNode, level + 1, self);
  479. }
  480. });
  481. },
  482. /**
  483. * 向上传递
  484. * @param callback 执行方法,如果返回false,则中断执行
  485. * @param advance 是否先执行一次
  486. * @param self 自身
  487. */
  488. transferParent: function (callback, advance, self) {
  489. if (!self) {
  490. self = this;
  491. }
  492. if (this !== self || advance) {
  493. var goOn = callback && callback(this);
  494. if (goOn === false) {
  495. return;
  496. }
  497. }
  498. this.parentNode && this.parentNode.transferParent(callback, advance, self);
  499. },
  500. /**
  501. * 向下传递
  502. * @param callback 执行的方法,如果返回false,则中断执行
  503. * @param advance 是否先执行一次
  504. * @param self 自身
  505. */
  506. transferChildren: function (callback, advance, self) {
  507. if (!self) {
  508. self = this;
  509. }
  510. if (this !== self || advance) {
  511. var goOn = callback && callback(this);
  512. if (goOn === false) {
  513. return;
  514. }
  515. }
  516. var children = this.getChildren();
  517. if (children && children.length > 0) {
  518. for (var index in children) {
  519. children[index].transferChildren(callback, advance, self);
  520. }
  521. }
  522. },
  523. /**
  524. * 设置级联值
  525. */
  526. selectedValue: function () {
  527. var nodeId = this.nodeId;
  528. var cascader = this.cascader;
  529. var multiple = this.props.multiple;
  530. var checkStrictly = this.props.checkStrictly;
  531. var leaf = this.leaf;
  532. if (!multiple && (leaf || checkStrictly)) {
  533. cascader._setActiveValue(nodeId, this);
  534. } else if (multiple) {
  535. var checkedNodeIds = cascader.data.checkedNodeIds;
  536. var checkedNodes = cascader.data.checkedNodes;
  537. var disabledFixed = this.config.disabledFixed;
  538. var paths;
  539. if (checkStrictly) {
  540. var index = checkedNodeIds.indexOf(nodeId);
  541. if (index === -1) {
  542. paths = checkedNodes.concat([this]);
  543. } else {
  544. paths = checkedNodes.concat();
  545. paths.splice(index, 1);
  546. }
  547. } else {
  548. var allLeafChildren = this.getAllLeafChildren();
  549. var checked;
  550. if (this._checked !== 1 && disabledFixed) {
  551. checked = this._getMultipleChecked(allLeafChildren);
  552. } else {
  553. checked = this._checked;
  554. }
  555. if (checked === 1) {
  556. // 选中->未选中
  557. paths = checkedNodes.filter(function (node1) {
  558. return !allLeafChildren.some(function (node2) {
  559. return node1.nodeId === node2.nodeId;
  560. });
  561. });
  562. } else {
  563. // 未选中、部分选中->选中
  564. var add = allLeafChildren.filter(function (node) {
  565. return checkedNodeIds.indexOf(node.nodeId) === -1;
  566. });
  567. paths = checkedNodes.concat(add);
  568. }
  569. }
  570. var nodeIds = paths.map(function (node) {
  571. return node.nodeId;
  572. });
  573. cascader._setCheckedValue(nodeIds, paths);
  574. }
  575. },
  576. _liLoad: function (event, callback) {
  577. var leaf = this.leaf;
  578. var lazy = this.props.lazy;
  579. var lazyLoad = this.props.lazyLoad;
  580. var children = this.children;
  581. var self = this;
  582. var cascader = this.cascader;
  583. var level = this.level;
  584. var multiple = this.props.multiple;
  585. var checkStrictly = this.props.checkStrictly;
  586. if (!leaf && (!children || children.length === 0) && lazy) {
  587. if (!self.loading) {
  588. self.loading = true;
  589. lazyLoad(self, function (nodes) {
  590. self.loading = false;
  591. self.setChildren(cascader.initNodes(nodes, level + 1, self));
  592. self.children = nodes;
  593. callback && callback(event);
  594. // 多选&关联时,重新同步下父级节点的样式
  595. multiple && !checkStrictly && self.transferParent(function (node) {
  596. node.syncStyle();
  597. }, true);
  598. });
  599. }
  600. } else {
  601. callback && callback(event);
  602. }
  603. },
  604. /**
  605. * 点击li事件
  606. * @param callback
  607. * @private
  608. */
  609. _liClick: function (callback) {
  610. var leaf = this.leaf;
  611. var $li = this.$li;
  612. var self = this;
  613. function load(event) {
  614. self._liLoad(event, callback);
  615. }
  616. if (this.props.expandTrigger === "click" || leaf) {
  617. $li.click(load);
  618. }
  619. if (this.props.expandTrigger === "hover") {
  620. $li.mouseenter(load);
  621. }
  622. },
  623. setChildren: function (children) {
  624. this.childrenNode = children;
  625. },
  626. getChildren: function () {
  627. return this.childrenNode;
  628. },
  629. /**
  630. * 同步样式
  631. */
  632. syncStyle: function () {
  633. var multiple = this.props.multiple;
  634. var checkStrictly = this.props.checkStrictly;
  635. if (multiple) {
  636. //多选
  637. if (checkStrictly) {
  638. this._sync.syncMultipleCheckStrictly(this);
  639. } else {
  640. this._sync.syncMultiple(this);
  641. }
  642. } else {
  643. //单选
  644. if (checkStrictly) {
  645. this._sync.syncRadioCheckStrictly(this);
  646. } else {
  647. this._sync.syncRadio(this);
  648. }
  649. }
  650. },
  651. /**
  652. * 同步本节点样式
  653. */
  654. _sync: {
  655. /**
  656. * 同步单选关联样式
  657. */
  658. syncRadio: function (self) {
  659. var $li = self.$li;
  660. var fromIcon = self.icons.from;
  661. var okIcon = self.icons.ok;
  662. var multiple = self.props.multiple;
  663. var checkStrictly = self.props.checkStrictly;
  664. var nodeId = self.nodeId;
  665. if (!$li || multiple || checkStrictly) {
  666. return;
  667. }
  668. var activeNode = self.cascader.data.activeNode;
  669. if (self.activeNodeId === nodeId) {
  670. var ok = $li.find('.' + okIcon);
  671. if (ok.length === 0) {
  672. $li.prepend('<i class="' + fromIcon + ' ' + okIcon + ' el-cascader-node__prefix"></i>');
  673. }
  674. } else {
  675. $li.find('.' + okIcon).remove();
  676. }
  677. if (activeNode && activeNode.path.some(function (node) {
  678. return node.nodeId === nodeId;
  679. })) {
  680. $li.addClass('is-active');
  681. $li.addClass('in-checked-path');
  682. } else {
  683. $li.removeClass('is-active');
  684. $li.removeClass('in-checked-path');
  685. }
  686. },
  687. /**
  688. * 同步单选非关联样式
  689. */
  690. syncRadioCheckStrictly: function (self) {
  691. var $li = self.$li;
  692. var checkStrictly = self.props.checkStrictly;
  693. var multiple = self.props.multiple;
  694. if (!$li || multiple || !checkStrictly) {
  695. return;
  696. }
  697. var $radio = self.$radio;
  698. var activeNode = self.cascader.data.activeNode;
  699. var nodeId = self.nodeId;
  700. if (self.activeNodeId === nodeId) {
  701. $radio.find('.el-radio__input').addClass('is-checked');
  702. } else {
  703. $radio.find('.el-radio__input').removeClass('is-checked');
  704. }
  705. if (activeNode && activeNode.path.some(function (node) {
  706. return node.nodeId === nodeId;
  707. })) {
  708. $li.addClass('is-active');
  709. $li.addClass('in-checked-path');
  710. } else {
  711. $li.removeClass('is-active');
  712. $li.removeClass('in-checked-path');
  713. }
  714. },
  715. /**
  716. * 同步多选关联样式
  717. */
  718. syncMultiple: function (self) {
  719. var $li = self.$li;
  720. var checkStrictly = self.props.checkStrictly;
  721. var multiple = self.props.multiple;
  722. var disabledFixed = self.config.disabledFixed;
  723. if (!multiple || checkStrictly) {
  724. return;
  725. }
  726. var allLeafChildren = self.getAllLeafChildren(disabledFixed);
  727. // 全部未选中 0
  728. // 全部选中 1
  729. // 部分选中 2
  730. var checked = self._getMultipleChecked(allLeafChildren);
  731. self._checked = checked;
  732. if (!$li) {
  733. return;
  734. }
  735. var $checkbox = self.$checked.find('.el-checkbox__input');
  736. if (checked === 0) {
  737. $checkbox.removeClass('is-checked');
  738. $checkbox.removeClass('is-indeterminate');
  739. } else if (checked === 1) {
  740. $checkbox.removeClass('is-indeterminate');
  741. $checkbox.addClass('is-checked');
  742. } else if (checked === 2) {
  743. $checkbox.removeClass('is-checked');
  744. $checkbox.addClass('is-indeterminate');
  745. }
  746. },
  747. /**
  748. * 同步多选非关联样式
  749. */
  750. syncMultipleCheckStrictly: function (self) {
  751. var $li = self.$li;
  752. var checkStrictly = self.props.checkStrictly;
  753. var multiple = self.props.multiple;
  754. if (!$li || !multiple || !checkStrictly) {
  755. return;
  756. }
  757. var checkedNodes = self.cascader.data.checkedNodes;
  758. var checkedNodeIds = self.checkedNodeIds;
  759. var nodeId = self.nodeId;
  760. var exist = checkedNodes.some(function (node) {
  761. return node.path.some(function (node) {
  762. return node.nodeId === nodeId;
  763. });
  764. });
  765. var $checkbox = self.$checked.find('.el-checkbox__input');
  766. if (checkedNodeIds.some(function (checkedNodeId) {
  767. return checkedNodeId === nodeId;
  768. })) {
  769. // 选中
  770. $checkbox.addClass('is-checked');
  771. } else {
  772. // 未选中
  773. $checkbox.removeClass('is-checked');
  774. }
  775. if (exist) {
  776. $li.addClass('in-checked-path');
  777. } else {
  778. $li.removeClass('in-checked-path');
  779. }
  780. }
  781. },
  782. /**
  783. * 获取叶子节点value值
  784. * @param disabled 是否包含禁用,默认不包含
  785. * @returns {Node[]|*[]}
  786. */
  787. getAllLeafChildren: function (disabled) {
  788. var leaf = this.leaf;
  789. if (leaf) {
  790. return [this];
  791. } else {
  792. var leafs = [];
  793. this.transferChildren(function (node) {
  794. if (node.disabled && !disabled) {
  795. return false;
  796. }
  797. node.leaf && leafs.push(node);
  798. });
  799. return leafs;
  800. }
  801. },
  802. /**
  803. * 展开当前节点
  804. */
  805. expandPanel: function () {
  806. var path = this.path;
  807. var cascader = this.cascader;
  808. path.forEach(function (node, index, array) {
  809. if (index !== array.length - 1) {
  810. var childrenNode = node.childrenNode;
  811. cascader._appendMenu(childrenNode, node.level + 1, node.parentNode);
  812. }
  813. });
  814. },
  815. _getMultipleChecked: function (leafNodes) {
  816. var cascader = this.cascader;
  817. var checkedNodeIds = cascader.data.checkedNodeIds;
  818. var allLeafIds = leafNodes.map(function (node) {
  819. return node.nodeId;
  820. });
  821. // 全部未选中 0
  822. // 全部选中 1
  823. // 部分选中 2
  824. var checked = 0;
  825. for (var i = 0; i < allLeafIds.length; i++) {
  826. var child = allLeafIds[i];
  827. if (checked === 2) {
  828. break;
  829. }
  830. if (checkedNodeIds.indexOf(child) !== -1) {
  831. if (i > 0 && checked !== 1) {
  832. checked = 2;
  833. } else {
  834. checked = 1;
  835. }
  836. } else {
  837. // 当全部选中时,则改为部分选中,否则全部未选中
  838. checked = checked === 1 ? 2 : 0;
  839. }
  840. }
  841. return checked;
  842. }
  843. };
  844. function Cascader(config) {
  845. this.config = $.extend(true, {
  846. elem: '', //绑定元素
  847. value: null, //预设值
  848. options: [], //可选项数据源,键名可通过 Props 属性配置
  849. empty: '暂无数据', //无匹配选项时的内容
  850. placeholder: '请选择',//输入框占位文本
  851. disabled: false, //是否禁用
  852. clearable: false, //是否支持清空选项
  853. showAllLevels: true, //输入框中是否显示选中值的完整路径
  854. collapseTags: false, //多选模式下是否折叠Tag
  855. minCollapseTagsNumber: 1, //最小折叠标签数
  856. separator: ' / ', //选项分隔符
  857. filterable: false, //是否可搜索选项
  858. filterMethod: function (node, keyword) {
  859. return node.path.some(function (node) {
  860. return node.label.indexOf(keyword) !== -1;
  861. });
  862. }, //自定义搜索逻辑,第一个参数是节点node,第二个参数是搜索关键词keyword,通过返回布尔值表示是否命中
  863. debounce: 300, //搜索关键词输入的去抖延迟,毫秒
  864. beforeFilter: function (value) {
  865. return true;
  866. },//筛选之前的钩子,参数为输入的值,若返回 false,则停止筛选
  867. popperClass: '', // 自定义浮层类名 string
  868. extendClass: false, //继承class样式
  869. extendStyle: false, //继承style样式
  870. disabledFixed: false, //固定禁用项,使禁用项不被清理删除,禁用项只能通过函数添加或初始值添加,默认禁用项不可被函数或初始值添加
  871. maxSize: 0, // 多选选中的最大数量,0表示不限制
  872. props: {
  873. strictMode: false, //严格模式,设置value严格按照层级结构.例如:[[1,2,3],[1,2,4]]
  874. expandTrigger: 'click', //次级菜单的展开方式 string click / hover 'click'
  875. multiple: false, //是否多选 boolean - false
  876. checkStrictly: false, //是否严格的遵守父子节点不互相关联 boolean - false
  877. lazy: false, //是否动态加载子节点,需与 lazyLoad 方法结合使用 boolean - false
  878. lazyLoad: function (node, resolve) {
  879. }, //加载动态数据的方法,仅在 lazy 为 true 时有效 function(node, resolve),node为当前点击的节点,resolve为数据加载完成的回调(必须调用)
  880. value: 'value', //指定选项的值为选项对象的某个属性值 string — 'value'
  881. label: 'label', //指定选项标签为选项对象的某个属性值 string — 'label'
  882. children: 'children', //指定选项的子选项为选项对象的某个属性值 string — 'children'
  883. disabled: 'disabled', //指定选项的禁用为选项对象的某个属性值 string — 'disabled'
  884. leaf: 'leaf' //指定选项的叶子节点的标志位为选项对象的某个属性值 string — 'leaf'
  885. }
  886. }, config);
  887. this.data = {
  888. nodeId: 1, //nodeId的自增值
  889. nodes: [], //存储Node对象
  890. menuData: [], //压入菜单的数据
  891. activeNodeId: null, //存放NodeId
  892. activeNode: null, //存放Node
  893. checkedNodeIds: [], //存放多个NodeId
  894. checkedNodes: [] //存放多个Node
  895. };
  896. // 面板是否展开
  897. this.showPanel = false;
  898. this.event = {
  899. // 值变更事件
  900. change: [],
  901. // 打开事件
  902. open: [],
  903. // 关闭事件
  904. close: []
  905. }
  906. // 是否正在搜索中
  907. this.filtering = false;
  908. // 初始化
  909. this._init();
  910. // 面板关闭事件id
  911. this.closeEventId = 0;
  912. // 是否进入maxSize模式
  913. this._maxSizeMode = false;
  914. }
  915. Cascader.prototype = {
  916. constructor: Cascader,
  917. get props() {
  918. return this.config.props;
  919. },
  920. get isFiltering() {
  921. return this.filtering;
  922. },
  923. set isFiltering(filtering) {
  924. if (this.filtering === filtering) {
  925. return;
  926. }
  927. this.filtering = !!filtering;
  928. var $panel = this.$panel;
  929. if (this.filtering) {
  930. $panel.find('.el-cascader-panel').hide();
  931. $panel.find('.el-cascader__suggestion-panel').show();
  932. } else {
  933. $panel.find('.el-cascader-panel').show();
  934. $panel.find('.el-cascader__suggestion-panel').hide();
  935. this.$tagsInput && this.$tagsInput.val('')
  936. }
  937. },
  938. set maxSizeMode(maxSizeMode) {
  939. if (this._maxSizeMode !== maxSizeMode) {
  940. this._maxSizeMode = maxSizeMode;
  941. this.refreshMenu();
  942. }
  943. },
  944. icons: {
  945. from: 'layui-icon',
  946. down: 'layui-icon-down',
  947. close: 'layui-icon-close',
  948. right: 'layui-icon-right',
  949. ok: 'layui-icon-ok',
  950. loading: 'layui-icon-loading-1 layui-anim layui-anim-rotate layui-anim-loop'
  951. },
  952. // 初始化
  953. _init: function () {
  954. this._checkConfig();
  955. // 初始化输入框
  956. this._initInput();
  957. // 初始化面板
  958. this._initPanel();
  959. // 初始化选项值
  960. this.setOptions(this.config.options);
  961. var self = this;
  962. // 监听滚动条
  963. $(window).scroll(function () {
  964. self._resetXY();
  965. });
  966. // 监听窗口
  967. $(window).resize(function () {
  968. self._resetXY();
  969. });
  970. // 点击事件,展开面板
  971. this.$div.click(function (event) {
  972. if (self.config.disabled) {
  973. return;
  974. }
  975. var show = self.showPanel;
  976. if (!show) {
  977. self.open();
  978. } else {
  979. self.close();
  980. }
  981. });
  982. },
  983. /**
  984. * 检查配置
  985. * @private
  986. */
  987. _checkConfig: function () {
  988. var elem = this.config.elem;
  989. if (!elem || $(elem).length === 0) {
  990. throw new Error("缺少elem节点选择器");
  991. }
  992. var maxSize = this.config.maxSize;
  993. if (typeof maxSize !== 'number' || maxSize < 0) {
  994. throw new Error("maxSize应是一个大于等于0的有效的number值");
  995. }
  996. if (!Array.isArray(this.config.options)) {
  997. throw new Error("options不是一个有效的数组");
  998. }
  999. },
  1000. /**
  1001. * 初始化根目录
  1002. * @private
  1003. */
  1004. _initRoot: function () {
  1005. var lazy = this.props.lazy;
  1006. var lazyLoad = this.props.lazyLoad;
  1007. var self = this;
  1008. var nodes = this.data.nodes;
  1009. if (nodes.length > 0 || !lazy) {
  1010. this._appendMenu(nodes, 0);
  1011. } else if (lazy) {
  1012. this._appendMenu(nodes, 0);
  1013. lazyLoad({
  1014. root: true,
  1015. level: 0
  1016. }, function (nodes) {
  1017. self.data.nodes = self.initNodes(nodes, 0, null);
  1018. self._appendMenu(self.data.nodes, 0);
  1019. });
  1020. }
  1021. },
  1022. /**
  1023. * 设置选项值
  1024. * @param options
  1025. */
  1026. setOptions: function (options) {
  1027. this.config.options = options;
  1028. // 初始化节点
  1029. this.data.nodes = this.initNodes(options, 0, null);
  1030. // 初始化根目录
  1031. this._initRoot();
  1032. // 初始化值
  1033. this.setValue(this.config.value);
  1034. },
  1035. // 面板定位
  1036. _resetXY: function () {
  1037. var $div = this.$div;
  1038. var offset = $div.offset();
  1039. var $panel = this.$panel;
  1040. if ($panel) {
  1041. var windowHeight = window.innerHeight;
  1042. var windowWidth = window.innerWidth;
  1043. var panelHeight = $panel.height();
  1044. var panelWidth = $panel.width();
  1045. var divHeight = $div.height();
  1046. var boundingClientRect = $div[0].getBoundingClientRect();
  1047. var $arrow = $panel.find('.popper__arrow');
  1048. // 距离右边界的偏差值
  1049. var offsetDiff = Math.min(windowWidth - boundingClientRect.x - panelWidth - 5, 0);
  1050. var bottomHeight = windowHeight - (boundingClientRect.top + divHeight);
  1051. if (bottomHeight < panelHeight && boundingClientRect.top > panelHeight + 20) {
  1052. $panel.attr('x-placement', 'top-start')
  1053. // 向上
  1054. $panel.css({
  1055. top: offset.top - 20 - panelHeight + 'px',
  1056. left: offset.left + offsetDiff + 'px'
  1057. });
  1058. } else {
  1059. $panel.attr('x-placement', 'bottom-start');
  1060. // 距离底部边界的偏差值
  1061. var yOffset = Math.max(panelHeight - (windowHeight - boundingClientRect.y - divHeight - 15), 0);
  1062. // 向下
  1063. $panel.css({
  1064. top: offset.top + divHeight - yOffset + 'px',
  1065. left: offset.left + offsetDiff + 'px'
  1066. });
  1067. }
  1068. // 箭头偏移
  1069. $arrow.css("left", 35 - offsetDiff + "px");
  1070. }
  1071. },
  1072. get $menus() {
  1073. return this.$panel && this.$panel.find('.el-cascader-panel .el-cascader-menu');
  1074. },
  1075. // 初始化输入框
  1076. _initInput: function () {
  1077. var $e = $(this.config.elem);
  1078. var self = this;
  1079. // 当绑定的元素带有value属性,并且对象未设置值时,设置一个初始值
  1080. if (this.config.value === null && $e.attr('value')) {
  1081. this.config.value = $e.attr('value');
  1082. }
  1083. var placeholder = this.config.placeholder;
  1084. var fromIcon = this.icons.from;
  1085. var downIcon = this.icons.down;
  1086. var multiple = this.props.multiple;
  1087. var extendClass = this.config.extendClass;
  1088. var extendStyle = this.config.extendStyle;
  1089. this.$div = $('<div class="el-cascader"></div>');
  1090. if (extendStyle) {
  1091. var style = $e.attr('style');
  1092. if (style) {
  1093. this.$div.attr('style', style);
  1094. }
  1095. }
  1096. if (extendClass) {
  1097. var className = $e.attr('class');
  1098. if (className) {
  1099. className.split(' ').forEach(function (name) {
  1100. self.$div.addClass(name);
  1101. });
  1102. }
  1103. }
  1104. this.$input = $('<div class="el-input el-input--suffix">' +
  1105. '<input type="text" readonly="readonly" autocomplete="off" placeholder="' + placeholder + '" class="el-input__inner">' +
  1106. '<span class="el-input__suffix">' +
  1107. '<span class="el-input__suffix-inner">' +
  1108. '<i class="el-icon-arrow-down ' + fromIcon + ' ' + downIcon + '" style="font-size: 12px"></i>' +
  1109. '</span></span>' +
  1110. '</div>')
  1111. this.$div.append(this.$input);
  1112. this.$inputRow = this.$input.find('.el-input__inner');
  1113. // 多选标签
  1114. if (multiple) {
  1115. this.$tags = $('<div class="el-cascader__tags"><!----></div>');
  1116. this.$div.append(this.$tags);
  1117. }
  1118. this._initHideElement($e);
  1119. // 替换元素
  1120. $e.replaceWith(this.$div);
  1121. this.$icon = this.$input.find('i');
  1122. this._initFilterableInputEvent();
  1123. this.disabled(this.config.disabled);
  1124. },
  1125. /**
  1126. * 初始化隐藏元素input,主要用于layui的表单验证
  1127. * @param $e
  1128. * @private
  1129. */
  1130. _initHideElement: function ($e) {
  1131. // 保存原始元素
  1132. var attributes = $e[0].attributes;
  1133. var $input = $('<input />');
  1134. var keys = Object.keys(attributes);
  1135. for (var key in keys) {
  1136. var attribute = attributes[key];
  1137. $input.attr(attribute.name, attribute.value);
  1138. }
  1139. $input.hide();
  1140. $input.attr('type', 'hidden')
  1141. this.$ec = $input;
  1142. $e.before($input);
  1143. },
  1144. /**
  1145. * 初始化可搜索监听事件
  1146. * @private
  1147. */
  1148. _initFilterableInputEvent: function () {
  1149. var filterable = this.config.filterable;
  1150. if (!filterable) {
  1151. return;
  1152. }
  1153. var timeoutID;
  1154. var multiple = this.props.multiple;
  1155. var debounce = this.config.debounce;
  1156. var placeholder = this.config.placeholder;
  1157. var beforeFilter = this.config.beforeFilter;
  1158. var filterMethod = this.config.filterMethod;
  1159. var checkStrictly = this.props.checkStrictly;
  1160. var self = this;
  1161. function filter(event) {
  1162. var input = this;
  1163. if (timeoutID) {
  1164. clearTimeout(timeoutID);
  1165. }
  1166. timeoutID = setTimeout(function () {
  1167. timeoutID = null;
  1168. var val = $(input).val();
  1169. if (!val) {
  1170. self.isFiltering = false;
  1171. return;
  1172. }
  1173. self.open();
  1174. if (typeof beforeFilter === 'function' && beforeFilter(val)) {
  1175. self.isFiltering = true;
  1176. var nodes = self.getNodes();
  1177. var filterNodes = nodes.filter(function (node) {
  1178. var disabled;
  1179. if (checkStrictly) {
  1180. disabled = node.disabled;
  1181. } else {
  1182. disabled = node.path.some(function (node) {
  1183. return node.disabled;
  1184. });
  1185. }
  1186. if ((node.leaf || checkStrictly) && !disabled) {
  1187. if (typeof filterMethod === 'function' && filterMethod(node, val)) {
  1188. // 命中
  1189. return true;
  1190. }
  1191. }
  1192. return false;
  1193. });
  1194. self._setSuggestionMenu(filterNodes);
  1195. }
  1196. }, debounce);
  1197. }
  1198. if (multiple) {
  1199. // 多选可搜索
  1200. this.$tagsInput = $('<input type="text" autocomplete="off" placeholder="' + placeholder + '" class="el-cascader__search-input">');
  1201. var $tagsInput = this.$tagsInput;
  1202. this.$tags.append($tagsInput);
  1203. $tagsInput.on('keydown', filter);
  1204. $tagsInput.click(function (event) {
  1205. if (self.isFiltering) {
  1206. event.stopPropagation();
  1207. }
  1208. });
  1209. } else {
  1210. var $inputRow = this.$inputRow;
  1211. // 单选可搜索
  1212. $inputRow.removeAttr('readonly');
  1213. $inputRow.on('keydown', filter);
  1214. $inputRow.click(function (event) {
  1215. if (self.isFiltering) {
  1216. event.stopPropagation();
  1217. }
  1218. });
  1219. }
  1220. },
  1221. // 初始化面板(panel(1))
  1222. _initPanel: function () {
  1223. var $panel = this.$panel;
  1224. var popperClass = this.config.popperClass || '';
  1225. if (!$panel) {
  1226. // z-index:解决和layer.open默认19891016的冲突
  1227. 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>');
  1228. $panel = this.$panel;
  1229. $panel.appendTo('body');
  1230. $panel.click(function (event) {
  1231. // 阻止事件冒泡
  1232. event.stopPropagation();
  1233. });
  1234. // 初始化可搜索面板
  1235. this._initSuggestionPanel();
  1236. }
  1237. },
  1238. /**
  1239. * 添加菜单(panel(1)->menu(n))
  1240. * @param nodes 当前层级数据
  1241. * @param level 层级,从0开始
  1242. * @param parentNode 父级节点
  1243. * @param _menuItem 刷新时,传入的当前菜单的item数据
  1244. * @private
  1245. */
  1246. _appendMenu: function (nodes, level, parentNode, _menuItem) {
  1247. this._removeMenu(level);
  1248. if (parentNode && parentNode.leaf) {
  1249. return;
  1250. }
  1251. var menuData = this.data.menuData;
  1252. 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>');
  1253. // 重新添加菜单
  1254. this.$panel.find('.el-cascader-panel').append($div);
  1255. // 渲染细项
  1256. this._appendLi($div, nodes);
  1257. var menuItem = {nodes: nodes, level: level, parentNode: parentNode, scrollbar: {top: 0, left: 0}};
  1258. if (_menuItem) {
  1259. menuItem.scrollbar = _menuItem.scrollbar
  1260. }
  1261. // 渲染滚动条
  1262. this._initScrollbar($div, menuItem);
  1263. // 重新定位面板
  1264. this._resetXY();
  1265. menuData.push(menuItem);
  1266. },
  1267. /**
  1268. * 移除菜单
  1269. * @param level
  1270. * @private
  1271. */
  1272. _removeMenu: function (level) {
  1273. // 除了上一层的所有菜单全部移除
  1274. var number = level - 1;
  1275. if (number !== -1) {
  1276. this.$panel.find('.el-cascader-panel .el-cascader-menu:gt(' + number + ')').remove();
  1277. } else {
  1278. this.$panel.find('.el-cascader-panel .el-cascader-menu').remove();
  1279. }
  1280. // 保存菜单数据
  1281. var menuData = this.data.menuData;
  1282. if (menuData.length > level) {
  1283. menuData.splice(level, menuData.length - level);
  1284. }
  1285. },
  1286. /**
  1287. * 添加细项(panel(1)->menu(n)->li(n))
  1288. * @param $menu 当前菜单对象
  1289. * @param nodes 当前层级数据
  1290. * @private
  1291. */
  1292. _appendLi: function ($menu, nodes) {
  1293. var $list = $menu.find('.el-cascader-menu__list');
  1294. if (!nodes || nodes.length === 0) {
  1295. var isEmpty = this.config.empty;
  1296. $list.append('<div class="el-cascader-menu__empty-text">' + isEmpty + '</div>');
  1297. return;
  1298. }
  1299. $.each(nodes, function (index, node) {
  1300. node.bind($list);
  1301. });
  1302. },
  1303. /**
  1304. * 刷新菜单面板
  1305. */
  1306. refreshMenu: function () {
  1307. // 先复制一个数组,避免刷新菜单时,数组的数据被改变
  1308. var data = this.data.menuData.concat([]);
  1309. var self = this;
  1310. data.forEach(function (data) {
  1311. self._appendMenu(data.nodes, data.level, data.parentNode, data);
  1312. })
  1313. },
  1314. /**
  1315. * 初始化可搜索面板
  1316. * @private
  1317. */
  1318. _initSuggestionPanel: function () {
  1319. var filterable = this.config.filterable;
  1320. if (!filterable) {
  1321. return;
  1322. }
  1323. var $suggestionPanel = this.$suggestionPanel;
  1324. if (!$suggestionPanel) {
  1325. 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>');
  1326. $suggestionPanel = this.$suggestionPanel;
  1327. this.$panel.find('.popper__arrow').before($suggestionPanel);
  1328. $suggestionPanel.click(function (event) {
  1329. // 阻止事件冒泡
  1330. event.stopPropagation();
  1331. });
  1332. }
  1333. },
  1334. /**
  1335. * 设置可搜索菜单
  1336. * @param nodes
  1337. * @private
  1338. */
  1339. _setSuggestionMenu: function (nodes) {
  1340. var $suggestionPanel = this.$suggestionPanel;
  1341. var $list = $suggestionPanel.find('.el-cascader__suggestion-list');
  1342. $list.empty();
  1343. $suggestionPanel.find('.el-scrollbar__bar').remove();
  1344. if (!nodes || nodes.length === 0) {
  1345. $list.append('<li class="el-cascader__empty-text">无匹配数据</li>');
  1346. return;
  1347. }
  1348. $.each(nodes, function (index, node) {
  1349. node.bindSuggestion($list);
  1350. });
  1351. this._initScrollbar($suggestionPanel, {scrollbar: {top: 0, left: 0}});
  1352. this._resetXY();
  1353. },
  1354. /**
  1355. * 初始化节点数据
  1356. * @param data 原始数据
  1357. * @param level 层级
  1358. * @param parentNode 父级节点
  1359. * @returns {*[]}
  1360. */
  1361. initNodes: function (data, level, parentNode) {
  1362. var nodes = [];
  1363. for (var key in data) {
  1364. var datum = data[key];
  1365. var node = new Node(datum, this, level, parentNode);
  1366. nodes.push(node);
  1367. if (node.children && node.children.length > 0) {
  1368. node.setChildren(this.initNodes(node.children, level + 1, node));
  1369. }
  1370. }
  1371. return nodes;
  1372. },
  1373. /**
  1374. * 设置单选值
  1375. * @param nodeId 节点id
  1376. * @param node 节点对象
  1377. * @private
  1378. */
  1379. _setActiveValue: function (nodeId, node) {
  1380. if (this.data.activeNodeId !== nodeId) {
  1381. var activeNode = this.data.activeNode;
  1382. this.data.activeNodeId = nodeId;
  1383. this.data.activeNode = node;
  1384. activeNode && activeNode.transferParent(function (node) {
  1385. node.syncStyle();
  1386. }, true);
  1387. node && node.transferParent(function (node) {
  1388. node.syncStyle();
  1389. }, true);
  1390. // 填充路径
  1391. this.change(node && node.value, node);
  1392. if (nodeId !== null) {
  1393. this._setClear();
  1394. }
  1395. }
  1396. },
  1397. /**
  1398. * 设置多选值
  1399. * @param nodeIds 值数组
  1400. * @param nodes 节点数组
  1401. * @private
  1402. */
  1403. _setCheckedValue: function (nodeIds, nodes) {
  1404. var checkedNodes = this.data.checkedNodes;
  1405. var maxSize = this.config.maxSize;
  1406. var maxSizeMode;
  1407. if (nodeIds.length > 0 && maxSize !== 0 && nodeIds.length >= maxSize) {
  1408. nodeIds = nodeIds.slice(0, maxSize);
  1409. nodes = nodes.slice(0, maxSize);
  1410. maxSizeMode = true
  1411. } else {
  1412. maxSizeMode = false
  1413. }
  1414. this.data.checkedNodeIds = nodeIds || [];
  1415. this.data.checkedNodes = nodes || [];
  1416. var syncPath = [];
  1417. var syncNodeIds = [];
  1418. checkedNodes.forEach(function (node) {
  1419. node.path.forEach(function (node) {
  1420. if (syncNodeIds.indexOf(node.nodeId) === -1) {
  1421. syncPath.push(node);
  1422. syncNodeIds.push(node.nodeId);
  1423. }
  1424. });
  1425. });
  1426. nodes.forEach(function (node) {
  1427. node.path.forEach(function (node) {
  1428. if (syncNodeIds.indexOf(node.nodeId) === -1) {
  1429. syncPath.push(node);
  1430. syncNodeIds.push(node.nodeId);
  1431. }
  1432. });
  1433. });
  1434. syncPath.forEach(function (node) {
  1435. node.syncStyle();
  1436. });
  1437. // 填充路径
  1438. this.change(nodes.map(function (node) {
  1439. return node.value;
  1440. }), nodes);
  1441. this._setClear();
  1442. this.maxSizeMode = maxSizeMode;
  1443. },
  1444. /**
  1445. * 设置值
  1446. * @param value
  1447. */
  1448. setValue: function (value) {
  1449. if (this.data.activeNodeId || this.data.checkedNodeIds.length > 0) {
  1450. // 清空值
  1451. this.clearCheckedNodes();
  1452. }
  1453. if (!value) {
  1454. return;
  1455. }
  1456. var strictMode = this.props.strictMode;
  1457. if (strictMode) {
  1458. if (!Array.isArray(value)) {
  1459. throw new Error("严格模式下,value必须是一个包含父子节点数组结构.");
  1460. }
  1461. }
  1462. var nodes = this.getNodes(this.data.nodes);
  1463. var checkStrictly = this.props.checkStrictly;
  1464. var multiple = this.props.multiple;
  1465. var disabledFixed = this.config.disabledFixed;
  1466. if (multiple) {
  1467. var paths = nodes.filter(function (node) {
  1468. if ((checkStrictly || node.leaf) && (!node.disabled || disabledFixed)) {
  1469. if (strictMode) {
  1470. // 严格模式下
  1471. // some:命中一个就为true
  1472. // every:全部命中为true
  1473. return value.some(function (levelValue) {
  1474. if (!Array.isArray(levelValue)) {
  1475. throw new Error("多选严格模式下,value必须是一个二维数组结构.");
  1476. }
  1477. var path = node.path;
  1478. return levelValue.length === path.length && levelValue.every(function (rowValue, index) {
  1479. return path[index].value === rowValue;
  1480. });
  1481. })
  1482. } else {
  1483. return value.indexOf(node.value) !== -1;
  1484. }
  1485. }
  1486. return false;
  1487. });
  1488. var nodeIds = paths.map(function (node) {
  1489. return node.nodeId;
  1490. });
  1491. this._setCheckedValue(nodeIds, paths);
  1492. // 展开第一个节点
  1493. if (paths.length > 0) {
  1494. var first = paths[0];
  1495. first.expandPanel();
  1496. }
  1497. } else {
  1498. for (var i = 0; i < nodes.length; i++) {
  1499. var node = nodes[i];
  1500. if ((checkStrictly || node.leaf)) {
  1501. var is = false;
  1502. if (strictMode) {
  1503. // 严格模式下
  1504. // every:全部命中为true
  1505. var path = node.path;
  1506. is = value.length === path.length && value.every(function (rowValue, index) {
  1507. return path[index].value === rowValue;
  1508. });
  1509. } else if (node.value === value) {
  1510. is = true;
  1511. }
  1512. if (is) {
  1513. this._setActiveValue(node.nodeId, node);
  1514. // 展开节点
  1515. node.expandPanel();
  1516. break;
  1517. }
  1518. }
  1519. }
  1520. }
  1521. },
  1522. /**
  1523. * 递归获取扁平的节点
  1524. * @param nodes
  1525. * @param container
  1526. * @returns {*[]}
  1527. */
  1528. getNodes: function (nodes, container) {
  1529. if (!container) {
  1530. container = [];
  1531. }
  1532. if (!nodes) {
  1533. nodes = this.data.nodes;
  1534. }
  1535. var self = this;
  1536. nodes.forEach(function (node) {
  1537. container.push(node);
  1538. var children = node.getChildren();
  1539. if (children) {
  1540. self.getNodes(children, container);
  1541. }
  1542. });
  1543. return container;
  1544. },
  1545. /**
  1546. * 初始化滚动条
  1547. * @param $menu 菜单的dom节点对象
  1548. * @param menuItem 当前菜单数据
  1549. * @private
  1550. */
  1551. _initScrollbar: function ($menu, menuItem) {
  1552. 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>');
  1553. $menu.append($div);
  1554. var vertical = $($div[1]).find('.el-scrollbar__thumb');
  1555. var onhoriztal = $($div[0]).find('.el-scrollbar__thumb');
  1556. var scrollbar = $menu.find('.el-scrollbar__wrap');
  1557. var $panel = this.$panel;
  1558. var $lis = $menu.find('li');
  1559. var height = Math.max($panel.height(), $menu.height());
  1560. var hScale = (height - 6) / ($lis.height() * $lis.length);
  1561. var wScale = $panel.width() / $lis.width();
  1562. // 滚动条监听事件
  1563. function _scrollbarEvent(scrollTop, scrollLeft) {
  1564. if (hScale < 1) {
  1565. vertical.css('height', hScale * 100 + '%');
  1566. vertical.css('transform', 'translateY(' + scrollTop / $menu.height() * 100 + '%)');
  1567. }
  1568. if (wScale < 1) {
  1569. onhoriztal.css('width', wScale * 100 + '%');
  1570. onhoriztal.css('transform', 'translateY(' + scrollLeft / $menu.width() * 100 + '%)');
  1571. }
  1572. }
  1573. // 拖动事件
  1574. vertical.mousedown(function (event) {
  1575. event.stopImmediatePropagation();
  1576. event.stopPropagation();
  1577. // 禁止文本选择事件
  1578. var selectstart = function () {
  1579. return false;
  1580. };
  1581. $(document).bind("selectstart", selectstart);
  1582. var y = event.clientY;
  1583. var scrollTop = scrollbar.scrollTop();
  1584. // 移动事件
  1585. var mousemove = function (event) {
  1586. event.stopImmediatePropagation();
  1587. var number = scrollTop + (event.clientY - y) / hScale;
  1588. scrollbar.scrollTop(number);
  1589. };
  1590. $(document).bind('mousemove', mousemove);
  1591. // 鼠标松开事件
  1592. $(document).one('mouseup', function (event) {
  1593. event.stopPropagation();
  1594. event.stopImmediatePropagation();
  1595. $(document).off('mousemove', mousemove);
  1596. $(document).off('selectstart', selectstart);
  1597. });
  1598. });
  1599. // 监听滚动条事件
  1600. scrollbar.scroll(function () {
  1601. var scroll = $(this);
  1602. menuItem.scrollbar.top = scroll.scrollTop()
  1603. menuItem.scrollbar.left = scroll.scrollLeft()
  1604. _scrollbarEvent(menuItem.scrollbar.top, menuItem.scrollbar.left);
  1605. });
  1606. // 初始化滚动条
  1607. scrollbar.scrollTop(menuItem.scrollbar.top);
  1608. _scrollbarEvent(menuItem.scrollbar.top, menuItem.scrollbar.left);
  1609. },
  1610. // 填充路径
  1611. _fillingPath: function () {
  1612. var multiple = this.props.multiple;
  1613. var showAllLevels = this.config.showAllLevels;
  1614. var separator = this.config.separator;
  1615. var collapseTags = this.config.collapseTags;
  1616. var $inputRow = this.$inputRow;
  1617. var placeholder = this.config.placeholder;
  1618. var self = this;
  1619. if (!multiple) {
  1620. var activeNode = this.data.activeNode;
  1621. var path = activeNode && activeNode.path || [];
  1622. if (showAllLevels) {
  1623. this._$inputRowSetValue(path.map(function (node) {
  1624. return node.label;
  1625. }).join(separator));
  1626. } else {
  1627. this._$inputRowSetValue(activeNode && activeNode.label || "");
  1628. }
  1629. } else {
  1630. // 复选框
  1631. // 删除标签
  1632. this.$tags.find('.el-tag').remove();
  1633. var $tagsInput = this.$tagsInput;
  1634. // 清除高度
  1635. $inputRow.css('height', '');
  1636. var checkedNodes = this.data.checkedNodes;
  1637. var minCollapseTagsNumber = Math.max(this.config.minCollapseTagsNumber, 1);
  1638. if (checkedNodes.length > 0) {
  1639. var tags = [];
  1640. var paths = checkedNodes;
  1641. if (collapseTags) {
  1642. // 折叠tags
  1643. paths = checkedNodes.slice(0, Math.min(checkedNodes.length, minCollapseTagsNumber));
  1644. }
  1645. paths.forEach(function (node) {
  1646. tags.push(node.$tag);
  1647. });
  1648. // 判断标签是否折叠
  1649. if (collapseTags) {
  1650. // 判断标签最小折叠数
  1651. if (checkedNodes.length > minCollapseTagsNumber) {
  1652. tags.push(self.get$tag('+ ' + (checkedNodes.length - minCollapseTagsNumber), false));
  1653. }
  1654. }
  1655. tags.forEach(function (tag) {
  1656. if ($tagsInput) {
  1657. $tagsInput.before(tag)
  1658. } else {
  1659. self.$tags.append(tag);
  1660. }
  1661. });
  1662. }
  1663. var tagHeight = self.$tags.height();
  1664. var inputHeight = $inputRow.height();
  1665. if (tagHeight > inputHeight) {
  1666. $inputRow.css('height', tagHeight + 4 + 'px');
  1667. }
  1668. // 重新定位
  1669. this._resetXY();
  1670. if (checkedNodes.length > 0) {
  1671. $inputRow.removeAttr('placeholder');
  1672. $tagsInput && $tagsInput.removeAttr('placeholder', placeholder);
  1673. } else {
  1674. $inputRow.attr('placeholder', placeholder);
  1675. $tagsInput && $tagsInput.attr('placeholder', placeholder);
  1676. }
  1677. }
  1678. },
  1679. /**
  1680. * 设置单选输入框的值
  1681. * @param label
  1682. * @private
  1683. */
  1684. _$inputRowSetValue: function (label) {
  1685. label = label || "";
  1686. var $inputRow = this.$inputRow;
  1687. $inputRow.attr('value', label); //防止被重置
  1688. $inputRow.val(label);
  1689. },
  1690. /**
  1691. * 获取复选框标签对象
  1692. * @param label
  1693. * @param showCloseIcon 是否显示关闭的icon
  1694. * @returns {jQuery|HTMLElement|*}
  1695. */
  1696. get$tag: function (label, showCloseIcon) {
  1697. var fromIcon = this.icons.from;
  1698. var closeIcon = this.icons.close;
  1699. var icon = showCloseIcon ? '<i class="el-tag__close el-icon-close ' + fromIcon + ' ' + closeIcon + '"></i>' : '';
  1700. return $('<span class="el-tag el-tag--info el-tag--small el-tag--light"><span>' + label + '</span>' + icon + '</span>');
  1701. },
  1702. // 设置可清理
  1703. _setClear: function () {
  1704. var self = this;
  1705. function enter() {
  1706. self.$icon.removeClass(self.icons.down);
  1707. self.$icon.addClass(self.icons.close);
  1708. }
  1709. function out() {
  1710. self.$icon.removeClass(self.icons.close);
  1711. self.$icon.addClass(self.icons.down);
  1712. }
  1713. self.$div.mouseenter(function () {
  1714. enter();
  1715. });
  1716. self.$div.mouseleave(function () {
  1717. out();
  1718. });
  1719. self.$icon.off('click');
  1720. var multiple = this.props.multiple;
  1721. var clear;
  1722. if (multiple) {
  1723. clear = this.data.checkedNodeIds.length > 0;
  1724. } else {
  1725. clear = !!this.data.activeNodeId;
  1726. }
  1727. if (clear && !this.config.disabled && this.config.clearable) {
  1728. self.$icon.one('click', function (event) {
  1729. event.stopPropagation();
  1730. self.close();
  1731. self.clearCheckedNodes();
  1732. out();
  1733. self.$icon.off('mouseenter');
  1734. self.$div.off('mouseenter');
  1735. self.$div.off('mouseleave');
  1736. });
  1737. } else {
  1738. out();
  1739. self.$icon.off('mouseenter');
  1740. self.$div.off('mouseenter');
  1741. self.$div.off('mouseleave');
  1742. }
  1743. },
  1744. // 禁用
  1745. disabled: function (isDisabled) {
  1746. this.config.disabled = !!isDisabled;
  1747. if (this.config.disabled) {
  1748. this.$div.addClass('is-disabled');
  1749. this.$div.find('.el-input--suffix').addClass('is-disabled');
  1750. this.$inputRow.attr('disabled', 'disabled');
  1751. this.$tagsInput && this.$tagsInput.attr('disabled', 'disabled').hide()
  1752. } else {
  1753. this.$div.removeClass('is-disabled');
  1754. this.$div.find('.el-input--suffix').removeClass('is-disabled');
  1755. this.$inputRow.removeAttr('disabled');
  1756. this.$tagsInput && this.$tagsInput.removeAttr('disabled').show();
  1757. }
  1758. // 重新设置是否可被清理
  1759. this._setClear();
  1760. // 重新填充路径
  1761. this._fillingPath();
  1762. },
  1763. /**
  1764. * 当选中节点变化时触发 选中节点的值
  1765. * @param value 值
  1766. * @param node 节点
  1767. */
  1768. change: function (value, node) {
  1769. var multiple = this.props.multiple;
  1770. if (multiple) {
  1771. if (value && value.length > 0) {
  1772. this.$ec.attr('value', JSON.stringify(value));
  1773. // this.$ec.val(JSON.stringify(value));
  1774. } else {
  1775. this.$ec.removeAttr('value');
  1776. // this.$ec.val('');
  1777. }
  1778. } else {
  1779. this.$ec.attr('value', value || "");
  1780. // this.$ec.val(value);
  1781. }
  1782. // 填充路径
  1783. this._fillingPath();
  1784. this.event.change.forEach(function (e) {
  1785. typeof e === 'function' && e(value, node)
  1786. })
  1787. },
  1788. /**
  1789. * 当失去焦点时触发 (event: Event)
  1790. * @param eventId 不为空时,必须与closeEventId值相等,防止旧事件触发
  1791. */
  1792. close: function (eventId) {
  1793. if (this.showPanel && (!eventId || this.closeEventId === eventId)) {
  1794. this.showPanel = false;
  1795. this.$div.find('.layui-icon-down').removeClass('is-reverse');
  1796. this.$panel.slideUp(100);
  1797. this.visibleChange(false);
  1798. // 聚焦颜色
  1799. this.$input.removeClass('is-focus');
  1800. // 可搜索
  1801. var filterable = this.config.filterable;
  1802. if (filterable) {
  1803. this.isFiltering = false;
  1804. this._fillingPath();
  1805. }
  1806. this.event.close.forEach(function (e) {
  1807. typeof e === 'function' && e()
  1808. })
  1809. }
  1810. },
  1811. /**
  1812. * 当获得焦点时触发 (event: Event)
  1813. */
  1814. open: function () {
  1815. if (!this.showPanel) {
  1816. this.showPanel = true;
  1817. this.closeEventId++;
  1818. var self = this;
  1819. // 当前传播事件结束后,添加点击背景关闭面板事件
  1820. setTimeout(function () {
  1821. $(document).one('click', self.close.bind(self, self.closeEventId));
  1822. });
  1823. // 重新定位面板
  1824. this._resetXY();
  1825. // 箭头icon翻转
  1826. this.$div.find('.layui-icon-down').addClass('is-reverse');
  1827. this.$panel.slideDown(200);
  1828. this.visibleChange(true);
  1829. // 聚焦颜色
  1830. this.$input.addClass('is-focus');
  1831. this.event.open.forEach(function (e) {
  1832. typeof e === 'function' && e()
  1833. })
  1834. }
  1835. },
  1836. /**
  1837. * 下拉框出现/隐藏时触发
  1838. * @param visible 出现则为 true,隐藏则为 false
  1839. */
  1840. visibleChange: function (visible) {
  1841. },
  1842. /**
  1843. * 在多选模式下,移除Tag时触发 移除的Tag对应的节点的值
  1844. * @param tagValue 节点的值
  1845. * @param node 节点对象
  1846. */
  1847. removeTag: function (tagValue, node) {
  1848. },
  1849. /**
  1850. * 获取选中的节点值
  1851. * @returns {null|[]}
  1852. */
  1853. getCheckedValues: function () {
  1854. var strictMode = this.props.strictMode;
  1855. if (this.props.multiple) {
  1856. var checkedNodes = this.data.checkedNodes;
  1857. if (strictMode) {
  1858. return checkedNodes.map(function (node) {
  1859. return node.path.map(function (node1) {
  1860. return node1.value;
  1861. });
  1862. });
  1863. }
  1864. return checkedNodes.map(function (node) {
  1865. return node.value;
  1866. });
  1867. } else {
  1868. var activeNode = this.data.activeNode;
  1869. if (strictMode) {
  1870. return activeNode && activeNode.path.map(function (node) {
  1871. return node.value;
  1872. })
  1873. }
  1874. return activeNode && activeNode.value;
  1875. }
  1876. },
  1877. /**
  1878. * 获取选中的节点
  1879. * @returns {null|[]}
  1880. */
  1881. getCheckedNodes: function () {
  1882. var strictMode = this.props.strictMode;
  1883. if (this.props.multiple) {
  1884. var checkedNodes = this.data.checkedNodes;
  1885. if (strictMode) {
  1886. return checkedNodes && checkedNodes.map(function (node) {
  1887. return node.path;
  1888. });
  1889. }
  1890. return checkedNodes;
  1891. } else {
  1892. var activeNode = this.data.activeNode;
  1893. if (strictMode) {
  1894. return activeNode && activeNode.path;
  1895. }
  1896. return activeNode;
  1897. }
  1898. },
  1899. /**
  1900. * 清空选中的节点
  1901. * @param force 强制清理禁用固定节点
  1902. */
  1903. clearCheckedNodes: function (force) {
  1904. var multiple = this.props.multiple;
  1905. if (multiple) {
  1906. var disabledFixed = this.config.disabledFixed;
  1907. if (!force && disabledFixed) {
  1908. //禁用项被固定,则过滤出禁用项的选值出来
  1909. var checkedNodes = this.data.checkedNodes;
  1910. var disNodes = checkedNodes.filter(function (node) {
  1911. return node.disabled;
  1912. });
  1913. var nodeIds = disNodes.map(function (node) {
  1914. return node.nodeId;
  1915. });
  1916. this._setCheckedValue(nodeIds, disNodes);
  1917. } else {
  1918. this._setCheckedValue([], []);
  1919. }
  1920. } else {
  1921. this._setActiveValue(null, null);
  1922. }
  1923. }
  1924. };
  1925. var thisCas = function () {
  1926. var self = this;
  1927. return {
  1928. /**
  1929. * 设置选项值
  1930. * @param options
  1931. */
  1932. setOptions: function (options) {
  1933. self.setOptions(options);
  1934. },
  1935. /**
  1936. * 覆盖当前值
  1937. * @param value 单选时传对象,多选时传数组
  1938. */
  1939. setValue: function (value) {
  1940. self.setValue(value);
  1941. },
  1942. /**
  1943. * 当节点变更时,执行回调
  1944. * @param callback function(value,node){}
  1945. */
  1946. changeEvent: function (callback) {
  1947. self.event.change.push(callback);
  1948. },
  1949. /**
  1950. * 当面板关闭时,执行回调
  1951. * @param callback function(){}
  1952. */
  1953. closeEvent: function (callback) {
  1954. self.event.close.push(callback);
  1955. },
  1956. /**
  1957. * 当面板打开时,执行回调
  1958. * @param callback function(){}
  1959. */
  1960. openEvent: function (callback) {
  1961. self.event.open.push(callback);
  1962. },
  1963. /**
  1964. * 禁用组件
  1965. * @param disabled true/false
  1966. */
  1967. disabled: function (disabled) {
  1968. self.disabled(disabled);
  1969. },
  1970. /**
  1971. * 收起面板
  1972. */
  1973. close: function () {
  1974. self.close();
  1975. },
  1976. /**
  1977. * 展开面板
  1978. */
  1979. open: function () {
  1980. self.open();
  1981. },
  1982. /**
  1983. * 获取选中的节点,如需获取路径,使用node.path获取,将获取各级节点的node对象
  1984. * @returns {[]|*}
  1985. */
  1986. getCheckedNodes: function () {
  1987. return self.getCheckedNodes();
  1988. },
  1989. /**
  1990. * 获取选中的值
  1991. * @returns {[]|*}
  1992. */
  1993. getCheckedValues: function () {
  1994. return self.getCheckedValues();
  1995. },
  1996. /**
  1997. * 清空选中的节点
  1998. * @param force 强制清理禁用固定节点
  1999. */
  2000. clearCheckedNodes: function (force) {
  2001. self.clearCheckedNodes(force);
  2002. },
  2003. /**
  2004. * 展开面板到节点所在的层级
  2005. * @param value 节点值,只能传单个值,不允许传数组
  2006. */
  2007. expandNode: function (value) {
  2008. var nodes = self.getNodes(self.data.nodes);
  2009. for (var i = 0; i < nodes.length; i++) {
  2010. var node = nodes[i];
  2011. if (node.value === value) {
  2012. node.expandPanel();
  2013. break;
  2014. }
  2015. }
  2016. },
  2017. /**
  2018. * 获取当前配置副本
  2019. */
  2020. getConfig: function () {
  2021. return $.extend(true, {}, self.config);
  2022. },
  2023. /**
  2024. * 获取数据对象副本
  2025. * @returns {*}
  2026. */
  2027. getData: function () {
  2028. return $.extend(true, {}, self.data);
  2029. }
  2030. };
  2031. };
  2032. exports('layCascader', function (option) {
  2033. var ins = new Cascader(option);
  2034. return thisCas.call(ins);
  2035. });
  2036. });