thr-linkage-1.0.js 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472
  1. /**
  2. * JQ多级联动通用插件
  3. * 初始化 $.linkAgeInit(options);
  4. * @param options
  5. * @version 1.0
  6. * @email 641453620@qq.com
  7. *
  8. * id,插件内部唯一Id
  9. * zIndex,层级,默认899
  10. * shade,遮罩,默认0.2。支持true,false,0~1
  11. * fadeTime,淡入淡出时间(毫秒)
  12. * dataModel,插件数据来源模式:http 通过url插件自动请求获取数据源,data 通过 “dataSource” 注入数据源
  13. * dataHttpUri,当 dataModel = http时,代表请求数据源的URL地址(使用GET请求方式)
  14. * dataHttpPidKeyName,当 dataModel = http时,请求 dataHttpUri 时携带当前选项Id的键值名称,默认 pid
  15. * dataHttpParams,当 dataModel = http时,需另外携带的请求参数
  16. * dataHttpResultHandle,当 dataModel = http时,请求数据成功后,将调用该方法进行数据二次处理,然后返回给插件使用
  17. * dataIndex, 联动数据层级,范围2~5,默认2
  18. * dataName,联动显示名称,默认:['一级' , '二级' , '三级']
  19. * dataSource,当 dataModel = data时,为数据源
  20. * dataOnePid,当 dataModel = data时,一级数据源Id,默认0
  21. * dataIdKey,数据源Id键值名,默认id
  22. * selectedValues,默认选中项,形式:[{id:442001001,name:'测试项1',route:id路径}],route可选
  23. * maxChecked,最大支持选中项数量,默认5
  24. * boxClickShow,选择后显示下一级列表,默认true。支持true,false
  25. * showHtml,Box主体模板
  26. * showListHtml,下拉框主体Box html
  27. * showSelectHtml,下拉选择行 html,模板可自定义。内置变量:{id},{name},{route}
  28. * showCheckHtml,单项选择 html,模板可自定义。内置变量:{id},{name},{route},{_on_}(高亮class)
  29. * showCheckedHtml,已选择项 html,模板可自定义。内置变量:{id},{name},{route}
  30. * showShadeHtml,遮罩 html
  31. * closeCallBack,关闭按钮回调方法,传入已选中的项
  32. * confirmCallBack,确认按钮回调方法,传入已选中的项
  33. * msgCallBack,提示信息处理方法,传入提示消息
  34. * @type {{id: number, zIndex: number, shade: boolean, fadeTime: number, dataModel: string, dataHttpUri: Array, dataHttpPidKeyName: string, dataHttpParams: {}, dataHttpResultHandle: (function(*): *), dataIndex: number, dataName: string[], dataSource: Array, dataOnePid: number, dataIdKey: string, selectedValues: Array, maxChecked: number, showHtml: string, showListHtml: string, showSelectHtml: string, showCheckHtml: string, showCheckedHtml: string, showShadeHtml: string, closeCallBack: string, confirmCallBack: string, msgCallBack: msgCallBack}}
  35. * @returns {*}
  36. */
  37. ;(function ($) {
  38. var instanceNums = 0;
  39. $.extend({
  40. linkAgeInit:function (options) {
  41. var defaults = {
  42. id:12138,
  43. zIndex:899,
  44. shade:0.2,
  45. fadeTime:600,
  46. boxTitle:'请选择地区',
  47. dataModel:'http',
  48. dataHttpUri:[],
  49. dataHttpPidKeyName:'pid',
  50. dataHttpParams:{},
  51. dataHttpResultHandle:function (res) {
  52. return res.data;
  53. },
  54. dataIndex:2,
  55. dataName:['一级' , '二级' , '三级'],
  56. dataSource:[],
  57. dataOnePid:0,
  58. dataIdKey:'id',
  59. //已选择Id项
  60. selectedValues:[],
  61. maxChecked:5,
  62. boxClickShow:true,
  63. showHtml:' <div class="thr-linkage thr-box-{id}" style="z-index: {z_index};display:none;">\n' +
  64. ' <div class="thr-header">{boxTitle}\n' +
  65. ' <i class="thr-close thr-close-{id} fr lh30"></i>\n' +
  66. ' </div>\n' +
  67. ' <div class="thr-areas pd10">\n' +
  68. ' <h4 class="mtb5">您已选择<small class="fz12 fw">(最多可选择{maxChecked}个)</small><button class="fr thr-confirm">确定</button></h4>\n' +
  69. ' <dl class="fz14 thr-select-area">\n' +
  70. ' {selectedList}' +
  71. ' </dl>\n' +
  72. ' </div>\n' +
  73. '{selectList}' +
  74. ' <div class="thr-check-box fz14">\n' +
  75. ' <dl class="pd10">\n' +
  76. ' </dl>\n' +
  77. ' </div>\n' +
  78. ' </div>',
  79. showListHtml:'<div class="thr-list-area thr-list-{index} fz14 mt10 fl" data-index="{index}" {display}>\n' +
  80. ' <ul>\n' +
  81. ' <li class="fz16 thr-name">{name}</li>\n' +
  82. ' {volistItem}' +
  83. ' </ul>\n' +
  84. ' </div>',
  85. showSelectHtml:'<li data-id="{id}" data-route="{route}" class="">{name}</li>',
  86. showCheckHtml:'<dd data-id="{id}" data-route="{route}" class="{_on_}">{name}</dd>',
  87. showCheckedHtml:'<dd data-id="{id}" data-route="{route}" class="on">{name}</dd>',
  88. showShadeHtml:'<div class="thr-linkage-shade" style="display: block;background-color: rgba(0,0,0,{shadeNums});z-index: {z_index}"></div>',
  89. closeCallBack:'',
  90. confirmCallBack:'',
  91. msgCallBack:function (msg) {
  92. alert(msg);
  93. }
  94. };
  95. if (!options) options = {};
  96. //初始化配置
  97. options = $.extend(defaults, options);
  98. //唯一Id
  99. instanceNums++;
  100. options.id = options.id + instanceNums;
  101. if (options.dataIndex < 2) {
  102. console.log('Error : dataIndex 不能小于2!');
  103. return false;
  104. }
  105. try {
  106. //实例化
  107. return init(options);
  108. } catch (e) {
  109. console.log('Error : ' , e);
  110. return false;
  111. }
  112. },
  113. });
  114. //私有业务方法
  115. //实例化插件
  116. function init(options) {
  117. return {
  118. options:options,
  119. /**
  120. * 打开窗口
  121. * @param areaItem
  122. */
  123. open:function (areaItem) {
  124. var $this = this;
  125. var $thisSys = $this.options;
  126. //选择元素
  127. if (!areaItem) areaItem = 'body';
  128. //BoxId
  129. var thisBoxId = '.thr-box-'+$thisSys.id + ' ';
  130. //遮罩
  131. if ($thisSys.shade !== false) {
  132. var thisShadeTpl = $thisSys.showShadeHtml;
  133. var shadeNums = $thisSys.shade === true ? 0.2 : $thisSys.shade;
  134. thisShadeTpl = thisShadeTpl.replace(/{shadeNums}/g , shadeNums);
  135. thisShadeTpl = thisShadeTpl.replace(/{z_index}/g , $thisSys.zIndex-1);
  136. $(areaItem).append(thisShadeTpl);
  137. }
  138. //判断Box是否存在
  139. var thisStatus = $(".thr-box-" + $thisSys.id).css('display');
  140. if (thisStatus == 'none') {
  141. $(".thr-box-" + $thisSys.id).fadeIn($thisSys.fadeTime);
  142. //局部渲染
  143. if ($thisSys.selectedValues.constructor == Array && $thisSys.selectedValues.length > 0) {
  144. var thisSelectedIds = [];
  145. for (var i in $thisSys.selectedValues) {
  146. thisSelectedIds.push($thisSys.selectedValues[i].id);
  147. }
  148. $(thisBoxId + '.thr-select-area dd').each(function () {
  149. var $this = $(this);
  150. var $thisId = $(this).data('id');
  151. if (!in_array($thisId , thisSelectedIds)) {
  152. //丢弃选项
  153. $this.remove();
  154. //丢球选中
  155. $(thisBoxId + ".thr-check-box dd[data-id='"+$thisId+"']").removeClass('on');
  156. }
  157. })
  158. }
  159. return;
  160. }
  161. var thisHtml = $thisSys.showHtml;
  162. //替换
  163. thisHtml = thisHtml.replace(/{id}/g , $thisSys.id);
  164. thisHtml = thisHtml.replace(/{z_index}/g , $thisSys.zIndex);
  165. thisHtml = thisHtml.replace(/{boxTitle}/g , $thisSys.boxTitle);
  166. thisHtml = thisHtml.replace(/{maxChecked}/g , $thisSys.maxChecked);
  167. //遍历结构体
  168. thisHtml = thisHtml.replace(/{selectList}/g , loopSelectBoxInit($thisSys));
  169. //遍历已选择项
  170. if ($thisSys.selectedValues.constructor == Array) {
  171. var selectedHtml = '';
  172. for (var s in $thisSys.selectedValues) {
  173. var selectedTpl = $thisSys.showCheckedHtml;
  174. selectedTpl = selectedTpl.replace(/{id}/g , $thisSys.selectedValues[s].id);
  175. selectedTpl = selectedTpl.replace(/{name}/g , $thisSys.selectedValues[s].name);
  176. selectedTpl = selectedTpl.replace(/{route}/g , '');
  177. selectedHtml += selectedTpl;
  178. }
  179. thisHtml = thisHtml.replace(/{selectedList}/g , selectedHtml);
  180. } else {
  181. thisHtml = thisHtml.replace(/{selectedList}/g , '');
  182. }
  183. //输出
  184. $(areaItem).append(thisHtml);
  185. //显示
  186. $(thisBoxId).fadeIn($thisSys.fadeTime);
  187. //移除事件
  188. $('body').off('click' , thisBoxId + '.thr-list-area ul li');
  189. $('body').off('click' , thisBoxId + '.thr-check-box dl dd');
  190. $('body').off('click' , thisBoxId + '.thr-select-area dd');
  191. $('body').off('click' , thisBoxId + '.thr-confirm');
  192. $('body').off('click' , thisBoxId + '.thr-close-'+$thisSys.id);
  193. //绑定事件
  194. //下拉选择区点击
  195. $('body').on('click', thisBoxId + '.thr-list-area ul li:not(:first-child)' , function () {
  196. var $thisLi = $(this);
  197. var $index = $thisLi.parent().parent().data('index');
  198. var nextIndex = parseInt($index)+1;
  199. var $thisId = $thisLi.data('id');
  200. var $thisRoute = $thisLi.data('route');
  201. $thisLi.siblings().removeClass('on');
  202. $thisLi.addClass('on');
  203. if (nextIndex < $thisSys.dataIndex) {
  204. if ($thisSys.boxClickShow) {
  205. //显示
  206. $('.thr-box-'+$thisSys.id+' .thr-list-'+nextIndex).show();
  207. }
  208. var nextHtml = loopSelectBoxItem($thisId , $thisSys.showSelectHtml , nextIndex , $thisRoute , $thisSys);
  209. $('.thr-box-'+$thisSys.id+' .thr-list-'+nextIndex+' ul li.thr-name').nextUntil().remove();
  210. $('.thr-box-'+$thisSys.id+' .thr-list-'+nextIndex+' ul').append(nextHtml);
  211. return;
  212. }
  213. //最后一级
  214. var nextHtml = loopSelectBoxItem($thisId , $thisSys.showCheckHtml , nextIndex , $thisRoute , $thisSys);
  215. $('.thr-box-'+$thisSys.id+' .thr-check-box dl').html('').append(nextHtml);
  216. });
  217. //选择项事件
  218. $('body').on('click' , thisBoxId + '.thr-check-box dl dd' , function () {
  219. var $thisDd = $(this);
  220. var $thisId = $thisDd.data('id');
  221. var $thisRoute = $thisDd.data('route');
  222. var $thisName = $.trim($thisDd.text());
  223. if ($thisSys.selectedValues.constructor != Array) {
  224. $thisSys.selectedValues = [];
  225. }
  226. //验证已选择
  227. for (var s in $thisSys.selectedValues) {
  228. if ($thisId == $thisSys.selectedValues[s].id) {
  229. //删除操作
  230. $thisSys.selectedValues.splice(s , 1);
  231. $thisDd.removeClass('on');
  232. $(thisBoxId + ".thr-select-area dd[data-id='"+$thisId+"']").remove();
  233. return false;
  234. }
  235. }
  236. //验证最大选择数量
  237. if ($thisSys.selectedValues.length >= $thisSys.maxChecked) {
  238. if (typeof $thisSys.msgCallBack == 'function') {
  239. $thisSys.msgCallBack('您最多只可选择 '+$thisSys.maxChecked+' 个哦!');
  240. }
  241. return false;
  242. }
  243. //添加
  244. var thisTpl = $thisSys.showCheckedHtml;
  245. thisTpl = thisTpl.replace(/{id}/g , $thisId);
  246. thisTpl = thisTpl.replace(/{name}/g , $thisName);
  247. thisTpl = thisTpl.replace(/{route}/g , $thisRoute);
  248. $(thisBoxId + '.thr-select-area').append(thisTpl);
  249. $thisSys.selectedValues.push({id:$thisId,name:$thisName,route:$thisRoute});
  250. $thisDd.addClass('on');
  251. });
  252. //点击删除项
  253. $('body').on('click' , thisBoxId + '.thr-select-area dd' , function () {
  254. var $thisDd = $(this);
  255. var $thisId = $thisDd.data('id');
  256. if ($thisSys.selectedValues.constructor != Array) {
  257. $thisSys.selectedValues = [];
  258. return false;
  259. }
  260. //删除数据
  261. for (var s in $thisSys.selectedValues) {
  262. if ($thisId == $thisSys.selectedValues[s].id) {
  263. //删除
  264. $thisSys.selectedValues.splice(s , 1);
  265. }
  266. }
  267. $thisDd.remove();
  268. $(thisBoxId + ".thr-check-box dl dd[data-id='"+$thisId+"']").removeClass('on');
  269. });
  270. //确定事件
  271. $('body').on('click' , thisBoxId + '.thr-confirm' , function () {
  272. $(".thr-linkage-shade").remove();
  273. $(".thr-box-" + $thisSys.id).fadeOut($thisSys.fadeTime);
  274. //回调
  275. if (typeof $thisSys.confirmCallBack == 'function') {
  276. $thisSys.confirmCallBack($thisSys.selectedValues);
  277. }
  278. });
  279. //关闭
  280. $('body').on('click' , thisBoxId + '.thr-close-'+$thisSys.id , function () {
  281. $(".thr-linkage-shade").remove();
  282. $(".thr-box-" + $thisSys.id).fadeOut($thisSys.fadeTime , function () {
  283. $(this).remove();
  284. });
  285. //回调
  286. if (typeof $thisSys.closeCallBack == 'function') {
  287. $thisSys.closeCallBack($thisSys.selectedValues);
  288. }
  289. //清空已选数据
  290. $thisSys.selectedValues = [];
  291. });
  292. },
  293. /**
  294. * 注入已选数据,格式:[{id:1,name:名称,route:id路径}]
  295. * @param selected
  296. */
  297. set:function (selected) {
  298. var $this = this;
  299. if (selected && selected.constructor == Array) {
  300. $this.options.selectedValues = selected;
  301. return;
  302. }
  303. $this.options.selectedValues = [];
  304. }
  305. };
  306. }
  307. /**
  308. * 根据配置遍历单项下拉选择数据(初始化)
  309. * @param thisSys
  310. */
  311. function loopSelectBoxInit(thisSys) {
  312. var thisList = '';
  313. for (var i=0;i<(thisSys.dataIndex-1);i++) {
  314. var volistItem = '';
  315. var thisListHtml = thisSys.showListHtml;
  316. thisListHtml = thisListHtml.replace(/{index}/g , (i+1));
  317. thisListHtml = thisListHtml.replace(/{name}/g , thisSys.dataName[i]);
  318. //初始化
  319. if (i == 0) {
  320. thisListHtml = thisListHtml.replace(/{display}/g , '');
  321. volistItem = loopSelectBoxItem(thisSys.dataOnePid , thisSys.showSelectHtml , (i+1) , '' , thisSys);
  322. } else {
  323. thisListHtml = thisListHtml.replace(/{display}/g , thisSys.boxClickShow ? 'style="display:none;"' : '');
  324. volistItem = '';
  325. }
  326. thisListHtml = thisListHtml.replace(/{volistItem}/ , volistItem)
  327. thisList += thisListHtml;
  328. }
  329. return thisList;
  330. }
  331. function loopSelectBoxItem(thisPid , thisHtml , thisIndex , thisRoute , thisSys){
  332. var thisData = [];
  333. var thisSelectIdValue = [];
  334. if (thisSys.selectedValues.constructor == Array) {
  335. for (var s in thisSys.selectedValues) {
  336. thisSelectIdValue.push(thisSys.selectedValues[s].id);
  337. }
  338. }
  339. switch (thisSys.dataModel) {
  340. case 'http':
  341. if (thisSys.dataHttpUri.constructor == Array) {
  342. var thisUrl = thisSys.dataHttpUri[thisIndex-1];
  343. } else {
  344. var thisUrl = thisSys.dataHttpUri;
  345. }
  346. if (isNull(thisUrl)) {
  347. throw new Error('dataHttpUri Error : data error !');
  348. }
  349. //构造请求参数
  350. var requestData = {};
  351. requestData[thisSys.dataHttpPidKeyName] = thisPid;
  352. requestData = $.extend(requestData , thisSys.dataHttpParams);
  353. //请求
  354. $.ajax({
  355. async:false,
  356. type:'GET',
  357. url:thisUrl,
  358. data:requestData,
  359. error:function () {},
  360. success:function (res) {
  361. if (typeof thisSys.dataHttpResultHandle == 'function') {
  362. thisData = thisSys.dataHttpResultHandle(res);
  363. }
  364. }
  365. });
  366. break;
  367. case 'data':
  368. if (!(thisSys.dataSource).hasOwnProperty(thisPid)) {
  369. throw new Error('dataSource Error : '+thisPid+' Property does not exist !');
  370. }
  371. thisData = thisSys.dataSource[thisPid];
  372. break;
  373. default:
  374. throw new Error('dataModel Error !');
  375. }
  376. if (isNull(thisData)) return '';
  377. //遍历html
  378. var returnHtml = '';
  379. for (var i in thisData) {
  380. var thisForHtml = thisHtml;
  381. if (thisData[i].constructor == Object) {
  382. for (var j in thisData[i]) {
  383. var thisExp = new RegExp('{'+j+'}' , 'g');
  384. thisForHtml = thisForHtml.replace(thisExp , thisData[i][j]);
  385. //选项高亮
  386. if (!isNull(thisSelectIdValue) && thisIndex == thisSys.dataIndex && in_array(thisData[i][thisSys.dataIdKey] , thisSelectIdValue)) {
  387. thisForHtml = thisForHtml.replace(/{_on_}/ , 'on');
  388. } else {
  389. thisForHtml = thisForHtml.replace(/{_on_}/ , '');
  390. }
  391. //路由Id
  392. var thisRouteTxt = '';
  393. if (isNull(thisRoute)) {
  394. thisRouteTxt = thisData[i][thisSys.dataIdKey];
  395. } else {
  396. thisRouteTxt = thisRoute + '-' + thisData[i][thisSys.dataIdKey];
  397. }
  398. thisForHtml = thisForHtml.replace(/{route}/ , thisRouteTxt);
  399. }
  400. } else {
  401. throw new Error('dataSource Error : ' , thisData[i]);
  402. }
  403. returnHtml += thisForHtml;
  404. }
  405. //返回
  406. return returnHtml;
  407. }
  408. //公共方法
  409. /**
  410. * 检验数据是否为 null,undefined,""
  411. * @param string
  412. * @returns {boolean}
  413. */
  414. function isNull(string) {
  415. string = $.trim(string);
  416. if (string === 0) {
  417. return false;
  418. }
  419. if(string == "" || string == undefined || string == null || string == "undefined") {
  420. return true;
  421. }
  422. return false;
  423. }
  424. /**
  425. * 搜索某个值是否存在数组之内
  426. * @param needle 搜索的值
  427. * @param haystack 被搜索的数组
  428. * */
  429. function in_array(needle, haystack) {
  430. if (haystack.constructor != Array) {
  431. haystack = String(haystack).split(',');
  432. }
  433. var length = haystack.length;
  434. for(var i = 0; i < length; i++) {
  435. if(haystack[i] == needle) return true;
  436. }
  437. return false;
  438. }
  439. })(jQuery);