0
0

cascader.js 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328
  1. /**
  2. * @Name: 基于layui的无限级联选择器
  3. * @Author: 李祥
  4. * @License:MIT
  5. * 最近修改时间: 2019/03/20
  6. */
  7. layui.define(["jquery","laytpl","layer"], function (exports) {
  8. var $ = layui.jquery;
  9. var laytpl = layui.laytpl;
  10. var layer = layui.layer;
  11. var zIndex=3000; // 共用一个层级
  12. function Cascader(option) {
  13. this.option=option; // 获取传入的数据
  14. this.domContent=""; // content节点
  15. this.textArr=[]; // 最终的text数组
  16. this.textStr=""; // 最终的text
  17. this.valueArr=[]; // 最终的value数组
  18. this.onOff=false; // 是否显示
  19. this.positionArr=[]; // 当前点击的面板在数据中的下标位置
  20. this.blockData={}; // 当前点击的当前面板的数据
  21. // this.count=0; // 进入finishInitData的次数
  22. this.initOption();
  23. }
  24. Cascader.prototype={
  25. constructor: Cascader,
  26. // 初始化参数数据
  27. initOption: function () {
  28. var self=this;
  29. self.option.elem?(function(){
  30. self.elem=self.option.elem;
  31. })():(function() {
  32. throw "缺少elem节点选择器";
  33. })();
  34. self.triggerType=self.option.triggerType==="change"?"mouseenter":"click";
  35. self.changeOnSelect=self.option.changeOnSelect || false;
  36. // 判断data参数
  37. if(self.option.data){
  38. self.d=self.option.data;
  39. self.callback();
  40. return;
  41. }
  42. // 判断url参数
  43. if(self.option.url){
  44. $.ajax({
  45. url: self.option.url,
  46. type: self.option.type?self.option.type:"get",
  47. data: self.option.where?self.option.where:{},
  48. success: function(data){
  49. if(data.Code===0){
  50. self.d=data.Data;
  51. self.callback();
  52. return;
  53. }
  54. layer.alert(data.Msg, { title: "选择器"+self.elem+"获取数据失败", icon: 2 });
  55. }
  56. });
  57. return;
  58. }
  59. throw "选择器"+self.elem+"缺少data或url参数";
  60. },
  61. // 初始化容器和标签
  62. init: function () {
  63. $(this.elem).after('<i class="layui-icon layui-icon-down"></i>');
  64. $(this.elem).after('<div class="urp-cascader-content"></div>');
  65. },
  66. // 初始化第一层
  67. initFirst: function () {
  68. var string = laytpl(
  69. '<ul class="urp-cascader-child">'+
  70. '{{# for(var i=0;i<d.length;i++){ }}'+
  71. '<li>{{ d[i].label }}<i class="layui-icon layui-icon-right" ></i></li>'+
  72. '{{# } }}'+
  73. '</ul>'
  74. ).render(this.d);
  75. $(this.elem).siblings(".urp-cascader-content").append(string);
  76. this.domContent=$(this.elem).siblings(".urp-cascader-content");
  77. this.domContent.find(".urp-cascader-child").hide();
  78. // 显示隐藏第一层的标签
  79. for(var i=0;i<this.d.length;i++){
  80. ("children" in this.d[i] && this.d[i]["children"].length>0)?(
  81. this.domContent.find("ul.urp-cascader-child li").eq(i).find("i").show()
  82. ):(
  83. this.domContent.find("ul.urp-cascader-child li").eq(i).find("i").hide()
  84. );
  85. }
  86. },
  87. // 获取当前点击的当前面板的数据
  88. getBlockData: function (event,el) {
  89. event.stopPropagation();
  90. this.floor=$(el).parent().index(); // 当前点击的是第几层
  91. var index=$(el).index(); // 当前点击的是这一层的第几个
  92. this.positionArr.length=this.floor;
  93. this.positionArr.push(index);
  94. // 等同下方注释
  95. this.blockData = this.d[this.positionArr[0]];
  96. for(var i = 1; i<=this.floor; i++){
  97. this.blockData = this.blockData["children"][this.positionArr[i]];
  98. }
  99. // switch (floor) {
  100. // case 0:
  101. // blockData=d[arr[0]];
  102. // break;
  103. // case 1:
  104. // blockData=d[arr[0]]["children"][arr[1]];
  105. // break;
  106. // case 2:
  107. // blockData=d[arr[0]]["children"][arr[1]]["children"][arr[2]];
  108. // break;
  109. // case 3:
  110. // blockData=d[arr[0]]["children"][arr[1]]["children"][arr[2]]["children"][arr[3]];
  111. // break;
  112. // default:
  113. // break;
  114. // }
  115. },
  116. // 若有第二层则初始化第二层
  117. initChild: function (triggerData) {
  118. // 删除后面的面板
  119. this.domContent.find(".urp-cascader-child:gt("+(this.floor)+")").remove();
  120. // 获取text值
  121. this.textArr.length=this.floor;
  122. this.textArr.push(this.blockData.label);
  123. this.valueArr.length=this.floor;
  124. this.valueArr.push(this.blockData.value);
  125. var string = laytpl(
  126. '<ul class="urp-cascader-child">'+
  127. '{{# for(var i=0;i< d.length;i++){ }}'+
  128. '<li>{{ d[i].label }}<i class="layui-icon layui-icon-right"></i></li>'+
  129. '{{# } }}'+
  130. '</ul>'
  131. ).render(this.blockData["children"]);
  132. this.domContent.append(string);
  133. // 显示隐藏第二层的标签
  134. for(var i=0;i<this.blockData["children"].length;i++){
  135. ("children" in this.blockData["children"][i] && this.blockData["children"][i]["children"].length>0)?(
  136. this.domContent.find("ul.urp-cascader-child:gt("+(this.floor)+")").find("li").eq(i).find("i").show()
  137. ):(
  138. this.domContent.find("ul.urp-cascader-child:gt("+(this.floor)+")").find("li").eq(i).find("i").hide()
  139. );
  140. }
  141. if(this.changeOnSelect){
  142. // 文本拼接
  143. this.textStr=this.textArr.join("/");
  144. $(this.elem).val(this.textStr);
  145. if(triggerData!=="initValue" && this.option.success) this.option.success(this.valueArr,this.textArr);
  146. }
  147. },
  148. // 结束之后拿取数据
  149. finishInitData: function (triggerData) {
  150. this.domContent.find(".urp-cascader-child:gt("+(this.floor)+")").remove();
  151. this.textArr.length=this.floor;
  152. this.textArr.push(this.blockData.label);
  153. this.valueArr.length=this.floor;
  154. this.valueArr.push(this.blockData.value);
  155. // 文本拼接
  156. this.textStr=this.textArr.join("/");
  157. (this.option.showLastLevels)?(
  158. $(this.elem).val(this.textArr[this.textArr.length-1])
  159. ):(
  160. $(this.elem).val(this.textStr)
  161. );
  162. this.onOff = false;
  163. $(this.elem).siblings(".urp-cascader-content").find("ul").slideUp(100);
  164. $(this.elem).siblings("i").replaceWith('<i class="layui-icon layui-icon-down"></i>');
  165. // 如果有初始值,则第一次不回调
  166. if(triggerData!=="initValue" && this.option.success) this.option.success(this.valueArr,this.textArr);
  167. // this.count++;
  168. // if($.isArray(this.option.value) && this.option.value.length>0 && this.count===1 && this.option.success){
  169. // return;
  170. // }
  171. // if(this.option.success) this.option.success(this.valueArr,this.textArr);
  172. },
  173. // 赋初值
  174. initValue: function() {
  175. var self=this;
  176. ($.isArray(this.option.value) && this.option.value.length>0)?(function(){
  177. var value=self.option.value;
  178. $(self.elem).trigger("click");
  179. var arrr=[]; // 保存当前在data中的位置
  180. var data=self.d; // 需要遍历的子数组
  181. // 等同于下面的注释
  182. value.forEach(function(val,index){
  183. // console.log(data);
  184. if(!data) throw "选择器"+self.elem+"初始化数据不匹配";
  185. for(var i=0;i<data.length;i++){
  186. if(data[i].value==val){
  187. arrr.push(i);
  188. self.domContent.find(".urp-cascader-child").eq(index).find("li").eq(i).trigger(self.triggerType,"initValue");
  189. $(self.elem).siblings(".urp-cascader-content").find("ul").finish(); // 停止当前正在运行的动画
  190. }
  191. }
  192. // 先判断数据是否存在,即是否有相匹配的数据
  193. data[arrr[index]]?(function(){
  194. data=data[arrr[index]].children;
  195. })():(function(){
  196. throw "选择器"+self.elem+"初始化数据不匹配";
  197. })()
  198. })
  199. // for(var i=0;i<d.length;i++){
  200. // if(d[i].value==value[0]){
  201. // arrr.push(i);
  202. // obj.domContent.find(".urp-cascader-child").eq(0).find("li").eq(i).trigger(triggerType);
  203. // }
  204. // }
  205. // for(var i=0;i<d[arrr[0]].children.length;i++){
  206. // if(d[arrr[0]].children[i].value==value[1]){
  207. // arrr.push(i)
  208. // obj.domContent.find(".urp-cascader-child").eq(1).find("li").eq(i).trigger(triggerType);
  209. // }
  210. // }
  211. // for(var i=0;i<d[arrr[0]].children[arrr[1]].children.length;i++){
  212. // if(d[arrr[0]].children[arrr[1]].children[i].value==value[2]){
  213. // obj.domContent.find(".urp-cascader-child").eq(2).find("li").eq(i).trigger(triggerType);
  214. // }
  215. // }
  216. })():"";
  217. },
  218. callback: function () {
  219. // 初始化第一层
  220. this.init();
  221. this.initFirst();
  222. var self=this; // Cascader对象
  223. // 每层点击时绑定事件
  224. self.domContent.on(self.triggerType,".urp-cascader-child li",function(event,triggerData){
  225. var _self=this; // 点击的对象
  226. self.getBlockData(event,this);
  227. $(this).addClass("active").siblings("li").removeClass("active");
  228. // 判断当前是否存在子层
  229. ("children" in self.blockData && self.blockData["children"].length>0)?(
  230. // 初始化子层
  231. self.initChild(triggerData)
  232. ):(
  233. // 判断触发方式
  234. self.triggerType==="mouseenter"?(function() {
  235. self.domContent.find(".urp-cascader-child:gt("+(self.floor)+")").remove();
  236. // click事件先解除再定义,防止多次定义
  237. $(_self).off("click").on("click",function() {
  238. self.finishInitData();
  239. })
  240. // 赋初值时若为change则需要触发上方函数(判断是否是通过赋初值方式触发)
  241. if(triggerData==="initValue"){
  242. $(_self).trigger("click");
  243. }
  244. })():(
  245. self.finishInitData(triggerData)
  246. )
  247. );
  248. })
  249. // input点击显示隐藏
  250. $(self.elem).on("click", function () {
  251. self.onOff = !self.onOff;
  252. zIndex++;
  253. if (self.onOff) {
  254. $(self.elem).siblings(".urp-cascader-content").find("ul").slideDown(100);
  255. $(self.elem).siblings("i").replaceWith('<i class="layui-icon layui-icon-up"></i>');
  256. self.domContent.css("zIndex",zIndex);
  257. } else {
  258. $(self.elem).siblings(".urp-cascader-content").find("ul").slideUp(100);
  259. $(self.elem).siblings("i").replaceWith('<i class="layui-icon layui-icon-down"></i>');
  260. }
  261. })
  262. // 点击外层文档隐藏
  263. $(document).on("click",function(event) {
  264. if(event.target.isEqualNode($(self.elem).get(0))) return;
  265. self.onOff = false;
  266. if(!self.onOff){
  267. $(self.elem).siblings(".urp-cascader-content").find("ul").slideUp(100);
  268. $(self.elem).siblings("i").replaceWith('<i class="layui-icon layui-icon-down"></i>');
  269. }
  270. })
  271. self.initValue();
  272. },
  273. reload: function(option) {
  274. var self=this;
  275. this.domContent.off();
  276. $(this.elem).off().siblings(".urp-cascader-content,.layui-icon").remove();
  277. this.option = $.extend({}, this.option, option);
  278. $(this.elem).val("");
  279. this.domContent="";
  280. this.textArr=[];
  281. this.textStr="";
  282. this.valueArr=[];
  283. this.onOff=false;
  284. this.positionArr=[];
  285. this.blockData={};
  286. return this.initOption()
  287. }
  288. }
  289. var thisCas=function() {
  290. var self=this;
  291. return {
  292. reload: function(option) {
  293. self.reload.call(self,option);
  294. }
  295. }
  296. }
  297. exports('cascader', function(option) {
  298. var ins=new Cascader(option);
  299. return thisCas.call(ins);
  300. });
  301. })