建立省-市-人員的三級選單
阿新 • • 發佈:2018-12-28
由於人員有兩三千,需求要按省-市-人員三級選單展示,便於勾選,起初的想法是ajax獲取使用者資料,然後迴圈按省市構造人員展示結構,但是測試發現數據量過大,導致頁面卡死。無奈只有採用後臺拼接好頁面結構的字串,然後前端獲取資料後在最外層div直接append的方式來提高展示速度。測試兩千多使用者展示在6-7秒。下面是簡要實現方式:
1、前端頁面:
<!DOCTYPE html > <%@include file="/common/jsp/header.jsp"%> <html> <head> <meta http-equiv="Content-Type" content="text/html; charset=utf-8" /> <title>選單首頁</title> <script type="text/javascript" src="${ctx}/webResources/js/jquery-3.3.1.min.js"></script> <script type="text/javascript" src="${ctx}/webResources/bootstrap-3.3.7/js/bootstrap.min.js"></script> <script type="text/javascript" src="${ctx}/webResources/js/jquery.itab.1.2.js"></script> <script type="text/javascript" src="${ctx}/webResources/js/sidebar-menu.js"></script> <style type="text/css"> a:link,a:visited{ text-decoration:none; /*超連結無下劃線*/ } a:hover{ text-decoration:underline; /*滑鼠放上去有下劃線*/ } li {list-style-type:none;margin:0px;white-space: nowrap;} .consureBtn { width: 30px; height: 25px; background: #0188fb; border: none; color: white; margin-left: 0px; margin-top:10px; position:absolute; bottom:0px; right:10px; } </style> </head> <script type="text/javascript"> $(document).ready(function(){ $("#part1").css("height","397px"); $(".btn-default").css("margin-top","-10px"); getUsers(); }); function getUsers(){ $(".menu ul").css("display","none"); $(".menu").html(''); //ajax獲取使用者資料 var url='showUsers.action'; var data={userId:$('#userId').val(),userName:$("#userName").val()}; $.ajax({ url:url, data:data, dataType:'json', async:false, //必須是同步,否則頁面在資料載入完成前就渲染結束 success:function(obj){ if(obj.result){ var arr=obj.data; $(".menuLeft").append(arr); }else{ alert(obj.message); } }, error:function(){ alert("資料載入失敗"); } }); initMenu(); } /* 放棄這種在頁面迴圈建立html結構的方式,採用後臺直接生成 function getUsers(){ $(".menu ul").css("display","none"); $(".menu").html(''); var url='showUsers.action'; var data={userId:$('#userId').val(),userName:$("#userName").val()}; $.ajax({ url:url, data:data, dataType:'json', async:false, //必須是同步 success:function(obj){ if(obj.result){ var arr=obj.data; for(var i=0;i<arr.length;i++){ var provEl='<li id="{provSec}"><input class="yeye" type="checkbox" value="selectAll" title="全選" onclick="choseThis(this)">' +'<a href="###"><img class="jiahao" alt="" src="${ctx}/common/img/jiahao.png" style="width:15px;height:15px;margin-right:6px;">' +'<span>{provName}</span></a><ul style="text-align:left;margin-left: -23px;"></ul></li>'; var cityEl='<li id="{citySec}"><input class="father" type="checkbox" value="selectAll" title="全選" onclick="choseThis(this)">' +'<a href="#"><img class="jiahao" alt="" src="${ctx}/common/img/jiahao.png" style="width:15px;height:15px;margin-right:6px;">' +'<span>{cityName}</span></a><ul style="text-align:left;margin-left: -22px;"></ul></li>'; var userEl='<li id="{userId}"><input class="child" type="checkbox" onclick="choseThis(this)"><a href="#">{userName}</a></li>'; var provId=arr[i].provId; var provName =arr[i].provName; var cityId = arr[i].cityId; var cityName=arr[i].cityName; var userId=arr[i].userId; var userName=arr[i].userName; //判斷省份節點是否存在存在就直接新增元素,否則就先建立省份節點再新增元素 if(!document.getElementById(provId)){ var str=provEl.replace("{provName}",provName).replace("{provSec}",provId); $(".menuLeft").append(str); } if(!document.getElementById(cityId)){ var str=cityEl.replace("{cityName}",cityName).replace("{citySec}",cityId); $("#"+provId).find('ul').eq(0).append(str); } var str=userEl.replace("{userId}",userId).replace("{userName}",userName); $("#"+cityId).find('ul').eq(0).append(str); } }else{ alert(obj.message); } }, error:function(){ alert("資料載入失敗"); } }); initMenu(); } */ function initMenu(){ $(".menu ul").css("display","none"); //檔案準備好後,ul的樣式為隱藏 $(".menu a").on("click",function(){ //點選a的時候就執行以下函式 $(this).next().toggle(function(){ if($(this).css("display")=='none'){ $(this).prev().children(".jiahao").attr("src","{ctx}/common/img/jiahao.png"); }else{ $(this).prev().children(".jiahao").attr("src","{ctx}/common/img/jianhao.png"); } }); //toggle提供為隱藏和顯示相互轉換的方法 意思是點選的這個 元素的下一個元素是顯示的話就隱藏,是隱藏的話就顯示 }); } </script> //引如勾選人員的操作js <script type="text/javascript" src="${ctx}/common/js/menuMove.js"></script> <body> <div id="tree" style="width:600px;text-align:center;"> <input type='hidden' value='${userId }' id='userId'> <div style="width:300px;float:left;border-right:1px solid #ccc;"> <input type="text" id="userName" style="width:200px;margin-left:18px;height:20px;padding-left:10px;border:1px solid #C7C7C7;"placeholder="搜尋人員"> <img src="${ctx }/common/img/sousuo.png" style="width:20px;height:20px;top:10px;cursor:pointer;position: absolute;margin-top:2px;" onclick="getUsers();"> <div class="queryUsers" style="width:230px;margin-top:0px;height:300px;overflow:auto;float:right;margin-right:10px;"> <ul class="menuLeft menu" style="margin-left:-40px;text-align:left;" vlaue='menuLeft'> <!-- --> <%-- <li id='1'><input class='yeye' type="checkbox" value="selectAll" title="全選" onclick="choseThis(this)"><a href="###"><img class="jiahao"alt="" src="${ctx }/common/img/jiahao.png" style="width:15px;height:15px;margin-right:6px;"><span>A系統</span></a> <ul style="text-align:left;margin-left: 103px;"> <li id='11'><input class='father' type="checkbox" value="selectAll" title="全選" onclick="choseThis(this)"><a href="#"><img class="jiahao"alt="" src="${ctx }/common/img/jiahao.png" style="width:15px;height:15px;margin-right:6px;"><span>部門1</span></a> <ul> <li id='111'><input class='child' type="checkbox" onclick="choseThis(this)"><a href="#">文件1</a></li> <li id='112'><input class='child' type="checkbox" onclick="choseThis(this)"><a href="#">文件2</a></li> </ul> </li> <li id='12'><input class='father' type="checkbox" id="c1" value="selectAll" title="全選" onclick="choseThis(this)"><a href="#"><img class="jiahao"alt="" src="${ctx }/common/img/jiahao.png" style="width:15px;height:15px;margin-right:6px;"><span>部門1</span></a> <ul> <li id='121'><input class='child' type="checkbox" onclick="choseThis(this)"><a href="#">文件111111111111111</a></li> <li id='122'><input class='child' type="checkbox" onclick="choseThis(this)"><a href="#">文件2</a></li> </ul> </li> </ul> </li> <li id='2'><input id='' class='yeye' type="checkbox" value="selectAll" title="全選" onclick="choseThis(this)"><a href="###"><img class="jiahao"alt="" src="${ctx }/common/img/jiahao.png" style="width:15px;height:15px;margin-right:6px;"><span>B系統</span></a> <ul> <li id='21'><input class='father' type="checkbox" value="selectAll" title="全選" onclick="choseThis(this)"><a href="#"><img class="jiahao"alt="" src="${ctx }/common/img/jiahao.png" style="width:15px;height:15px;margin-right:6px;"><span>部門1</span></a> <ul> <li id='211'><input class='child' type="checkbox" onclick="choseThis(this)"><a href="#">文件312</a></li> <li id='212'><input class='child' type="checkbox" onclick="choseThis(this)"><a href="#">文件321</a></li> </ul> </li> <li id='22'><input class='father' type="checkbox" id="c1" value="selectAll" title="全選" onclick="choseThis(this)"><a href="#"><img class="jiahao"alt="" src="${ctx }/common/img/jiahao.png" style="width:15px;height:15px;margin-right:6px;"><span>部門1</span></a> <ul> <li id='221'><input class='child' type="checkbox" onclick="choseThis(this)"><a href="#">文件21</a></li> <li id='222'><input class='child' type="checkbox" onclick="choseThis(this)"><a href="#">文件21</a></li> </ul> </li> </ul> </li> --%> </ul> </div> </div> <div style="width:200px;float:left;"> <div> <span>已選擇的人員</span> <img alt="" src="${ctx}/common/img/del.png" style="width:20px;height:20px;position:absolute;right:50px;cursor:pointer;" onmouseover="sureDel(this);" onmouseout="notDel(this);" onclick="deleteUsers();" title='移除'> </div> <div class="selectedUsers" style="width:250px;margin-top:0px;max-height:300px;overflow-y:auto;float:left;margin-left:25px;"> <ul class="menuRight menu"style="margin-left:-25px;text-align:left;" vlaue='menuRight'> <%-- <li><input type="checkbox" id="c1" value="selectAll" title="全選" onclick="choseThis(this);"><a href="###"><img class="jiahao"alt="" src="${ctx }/common/img/jiahao.png" style="width:15px;height:15px;margin-right:6px;"><span>A系統</span></a> <ul> <li><input type="checkbox" id="c1" value="" title="全選" value="selectAll" onclick="choseThis(this);"><a href="#"><img class="jiahao"alt="" src="${ctx }/common/img/jiahao.png" style="width:15px;height:15px;margin-right:6px;"><span>部門1</span></a> <ul> <li><input type="checkbox" onclick="choseThis(this);"><a href="#">文件1</a></li> <li><input type="checkbox" onclick="choseThis(this);"><a href="#">文件2</a></li> </ul> </li> </ul> </li> --%> </ul> </div> </div> <div> <button class="consureBtn " onclick="submitUsers()" id="addUser" style="width:80px;">提交</button> </div> </div> </body> </html>
前端頁面主要分為左右兩部分,左側用於展示使用者,右側展示左側勾選的使用者。
2、引入複製左側結構到右側的menuMove.js
//聯級選單移動 $(document).ready(function(){ /*$(".menu ul").css("display","none"); //檔案準備好後,ul的樣式為隱藏 $(".menu a").on("click",function(){ //點選a的時候就執行以下函式 $(this).next().toggle(function(){ if($(this).css("display")=='none'){ $(this).prev().children(".jiahao").attr("src","/${ctx}/common/img/jiahao.png"); }else{ $(this).prev().children(".jiahao").attr("src","/${ctx}/common/img/jianhao.png"); } }); //toggle提供為隱藏和顯示相互轉換的方法 意思是點選的這個 元素的下一個元素是顯示的話就隱藏,是隱藏的話就顯示 });*/ }); function choseThis(obj) { var value = $(obj).attr("value"); if (obj.checked == false) { if(value == 'selectAll'){ var li=$(obj).parent(); var checkboxs=li.find("input[type='checkbox']"); for(var i=0;i<checkboxs.length;i++){ checkboxs.eq(i).prop('checked',false); } } $(obj).parent().parent().prev().prev().prop("checked",false); $(obj).parent().parent().parent().parent().prev().prev().prop("checked",false); } else { //查詢 //leftToRight(obj); if (value == 'selectAll') { var li=$(obj).parent(); var checkboxs=li.find("input[type='checkbox']"); for(var i=0;i<checkboxs.length;i++){ if(checkboxs.eq(i).prop('checked')){ continue; } checkboxs.eq(i)[0].checked=true; } } var ul=$(obj).parent().parent(); var allLen=ul.find("input[type='checkbox']").length; var trueLen=ul .find("input[type='checkbox']:checked").length; if (allLen==trueLen) { ul.prev().prev().prop("checked",true); } var ulFather=ul.parent().parent(); if(ulFather.find("input[type='checkbox']").length==ulFather.find("input[type='checkbox']:checked").length){ ul.parent().parent().prev().prev().prop('checked',true); } //查詢 leftToRight(obj); } } //將左側的人員移到右側 function leftToRight(obj) { var cls=$(obj).attr("class"); if(cls=='yeye'){ if($(obj).parent().parent().attr("class").startsWith('menuRight')){ return; } var ul=$(obj).parent(); var id=ul.attr("id"); var menuRight=$('.menuRight').find('#'+id); if(menuRight.length!=0&&menuRight.find('input[type="checkbox"]:checked').length==ul.find('input[type="checkbox"]:checked').length){ return; } $('.menuRight').find('#'+id).remove(); ul=ul.clone(true); ul.children('input[type="checkbox"]:checked').prop("checked",false); //省下面的li ul.find('ul').css('margin-left','-22px'); //市下面的li ul.find('ul').find('li').find('ul').css('margin-left','-22px'); $('.menuRight').append(ul); }else if(cls=='father'){ if($(obj).parent().parent().parent().parent().attr("class").startsWith('menuRight')){ return; } var ulLi=$(obj).parent(); var li=$(obj).parent().parent().parent(); var id=li.attr("id"); var ul=$(obj).parent().parent(); var menuRightLi=$('.menuRight').children('#'+id); var id1=ulLi.attr('id'); if(menuRightLi.length!=0){ li=$('.menuRight').children('#'+id); ul=li.children("ul"); var childLi=ul.children("#"+id1); childLi.remove(); //console.log('===='+childLi.get(0)); ulLi=ulLi.clone(true); ulLi.children('input[type="checkbox"]:checked').prop('checked',false); ul.append(ulLi); ulLi=null; }else{ var input=ul.prev().prev().clone(true); var a=ul.prev().clone(true); li=li.clone(true); ul=ul.clone(true); ulLi=ulLi.clone(true); ulLi.children('input[type="checkbox"]:checked').prop('checked',false); //console.log(input.get(0)); //console.log(a.get(0)); ul.html(ulLi); //console.log(ul.get(0)); //console.log(ulLi.get(0)); li.html(''); li.append(input); li.append(a); li.append(ul); $('.menuRight').append(li); a=input=ul=ulLi=li=null; } $('.menuRight').find('ul').css('margin-left','-22px'); $('.menuRight').find('ul').find('li').find('ul').css('margin-left','-22px'); }else if(cls=='child'){ if($(obj).parent().parent().parent().parent().parent().parent().attr("class").startsWith('menuRight')){ return; } var li=$(obj).parent().clone(true); li.children('input[type="checkbox"]:checked').prop('checked',false); var ul=$(obj).parent().parent().clone(true); var inputChild=$(obj).parent().parent().prev().prev().clone(true); var aChild=$(obj).parent().parent().prev().clone(true); var ulFather=$(obj).parent().parent().parent().parent().clone(true); var input=$(obj).parent().parent().parent().parent().prev().prev().clone(true); var a=$(obj).parent().parent().parent().parent().prev().clone(true); var liFather=$(obj).parent().parent().parent().clone(true); var liYeye=$(obj).parent().parent().parent().parent().parent().clone(true); var yeyeId=liYeye.attr("id"); var righYes=$('.menuRight').children('#'+yeyeId); if(righYes.length>0){ var fatherId=liFather.attr("id"); var ul1=righYes.children('ul'); var liFather1=ul1.children('#'+fatherId); if(liFather1.length>0){ ul=liFather1.children('ul'); var id=li.attr('id'); var li1=ul.children('#'+id); if(li1.length>0){ //如果存在該子節點則無需操作 }else{ ul.append(li); } }else{ ul.html(''); ul.append(li); liFather.html(''); liFather.append(inputChild); liFather.append(aChild); liFather.append(ul); ul1.append(liFather); } }else{ ul.html(''); ul.append(li); liFather.html(''); liFather.append(inputChild); liFather.append(aChild); liFather.append(ul); ulFather.html(''); ulFather.append(liFather); liYeye.html(''); liYeye.append(input); liYeye.append(a); liYeye.append(ulFather); $('.menuRight').append(liYeye); } li=ul=inputChild=aChild=ulFather=input=a=liFather=liYeye=null; } } function sureDel(obj) { obj.src = '/${ctx}/common/img/del1.png'; } function notDel(obj) { obj.src = '/${ctx}/common/img/del.png'; } //將dom元素轉成字串 function domToString (node) { var tmpNode = $('<div></div>'); tmpNode.append(node.clone(true)); var str = tmpNode.html(); tmpNode = node = null; // 解除引用,以便於垃圾回收 return str; } function deleteUsers(){ var checkedUsers=$('.menuRight').find('input[type="checkbox"]:checked'); if(checkedUsers.length==0){ alert('請選擇需要刪除的人員'); return; } //ul:menuLeft var leftCheckeds=$('.menuLeft').find('input[type="checkbox"]:checked').parent().parent(); for(var i=0;i<checkedUsers.length;i++){ var id=checkedUsers.eq(i).parent().attr('id'); checkedUsers.eq(i).parent().remove(); //console.log(leftCheckeds.find('#'+id).attr('id')); leftCheckeds.find('#'+id).find('input[type="checkbox"]').prop('checked',false); //如果同級中沒有被選擇的checkbox就將父級置為未選擇 if(leftCheckeds.find('#'+id).parent().find('input[type="checkbox"]:checked').length==0){ leftCheckeds.parent().parent().find('input[type="checkbox"]').prop('checked',false); } //如果同級中沒有被選擇的checkbox就將父級置為未選擇 if(leftCheckeds.find('#'+id).parent().parent().parent().find('input[type="checkbox"]:checked').length==0){ leftCheckeds.find('#'+id).parent().parent().parent().parent().find('input[type="checkbox"]').prop('checked',false); } } }
3、後臺接收ajax請求的java方法,這裡也是重點,需要拼接好三級選單的結構:
首先是查詢資料庫獲取省份Id、省份名稱、地市Id、地市名稱、使用者Id、使用者名稱稱,然後分別將省份、地市和使用者的html結構放到map中用於後面的迴圈拼接。
@RequestMapping("/showUserMenu") public void showUsersOutGroup2(String userId,String userName){ //省去查詢資料的條件部分。。。 List<Object[]> modelList=this.userDataService.findUsers(paraMap); if(modelList.size()==0){ this.getResponseError("-1", "..."); return; } Map<String,Object> mapResults=new HashMap<String,Object>(); //儲存省-省的html結構的資料 Map<String,Object> provMap=new HashMap<String,Object>(); //儲存省-[市-市的html結構]的資料 Map<String,Map<String,Object>> cityMap=new HashMap<String,Map<String,Object>>(); //儲存省-[市-[市下面的使用者的html結構列表]] Map<String,Map<String,List<String>>> userMap=new HashMap<String,Map<String,List<String>>>(); for(Object[] o:modelList){ //Map<String,Object> map=new HashMap<String,Object>(); String provId=""; String cityId=""; if (null==o[3]) { provId="其他"; }else { provId=o[3].toString(); } if (null==o[4]) { cityId="其他"; }else { cityId=o[4].toString(); } //省份的html結構如下,省份的結構裡包含市的 String provEl="<li id=\""+provId+"\"><input class=\"yeye\" type=\"checkbox\" value=\"selectAll\" title=\"全選\" onclick=\"choseThis(this)\"><a href=\"###\"><img class=\"jiahao\" alt=\"\" src=\"${ctx}/common/img/jiahao.png\" style=\"width:15px;height:15px;margin-right:6px;\"><span>"+provId+"</span></a><ul style=\"text-align:left;margin-left: -23px;\">"; //市的html結構如下,市的html結構要包含使用者的 String cityEl="<li id=\""+cityId+"\"><input class=\"father\" type=\"checkbox\" value=\"selectAll\" title=\"全選\" onclick=\"choseThis(this)\"><a href=\"#\"><img class=\"jiahao\" alt=\"\" src=\"${ctx}/common/img/jiahao.png\" style=\"width:15px;height:15px;margin-right:6px;\"><span>"+cityId+"</span></a><ul style=\"text-align:left;margin-left: -22px;\">"; //使用者的html結構如下 String userEl="<li id=\""+o[0].toString()+"\"><input class=\"child\" type=\"checkbox\" onclick=\"choseThis(this)\"><a href=\"#\">"+o[1].toString()+"</a></li>"; //如果省份map不存在該省份的鍵就直接插入,否則不用插入 if (!provMap.containsKey(provId)) { provMap.put(provId, provEl); } //如果地市map沒有該省份的鍵就插入省份資料和新建地市map儲存 if (!cityMap.containsKey(provId)) { Map<String,Object> cityNode= new HashMap<String,Object>(); cityNode.put(cityId, cityEl); cityMap.put(provId, cityNode); }else { //如果地市map存在該省份的鍵就獲取該省份對應的地市map並判斷是否已經插入該地市,如果存在就不插入否則就插入 if (!cityMap.get(provId).containsKey(cityId)) { cityMap.get(provId).put(cityId, cityEl); } } //預設使用者資料沒有重複,直接插入 if (null!=userMap.get(provId)) { if (null!=userMap.get(provId).get(cityId)) { //避免重還是判斷一下存不存在重複 if (!userMap.get(provId).get(cityId).contains(userEl)) { userMap.get(provId).get(cityId).add(userEl); } }else { //如果省份節點存在,地市節點不存在 List<String> users=new ArrayList<String>(); users.add(userEl); userMap.get(provId).put(cityId, users); } }else { //如果不存在該省份的鍵先建立物件再插入值 Map<String,List<String>> cityNode= new HashMap<String,List<String>>(); List<String> users=new ArrayList<String>(); users.add(userEl); cityNode.put(cityId, users); userMap.put(provId, cityNode); } } //這個htmlStruc就是拼接的最終要到前端展示的三級選單的結構 String htmlStruc=createUserStruc(provMap,cityMap,userMap); mapResults.put("result", true); mapResults.put("data", htmlStruc); try { response.getWriter().println(JSONObject.fromObject(mapResults)); } catch (IOException e) { e.printStackTrace(); } } //下面是提取出來的將省市使用者html結構組合起來的方法 //將單獨儲存的省、市、人員html結構組合在一起 public String createUserStruc( Map<String,Object> provMap,Map<String,Map<String,Object>> cityMap,Map<String,Map<String,List<String>>> userMap){ //下面是拼接整個省-市-使用者的html結構 StringBuffer sb=new StringBuffer(); String prov=""; //遍歷省份節點,拼接每個省份的html結構 for (Map.Entry<String, Object> provEntry:provMap.entrySet()) { String provId=provEntry.getKey(); //如果臨時儲存省份id的變數的值和省份map獲取的key不一致說明到下一個省份了這時需要拼接上結束標籤 if (!provId.equals(prov)) { //prov為空說明還沒拼接前面部分,不需要拼接結束標籤 if (!"".equals(prov)) { sb.append("</ul></li>"); } prov=provId; } sb.append(provEntry.getValue()); //遍歷該省份對應的地市map for (Map.Entry<String,Object> cityEntry:cityMap.get(provId).entrySet()) { String cityId=cityEntry.getKey(); sb.append(cityEntry.getValue()); //獲取省-市對應的使用者list拼接使用者結構 List<String> users = userMap.get(provId).get(cityId); for (int i = 0; i < users.size(); i++ ) { sb.append(users.get(i)); } sb.append("</ul></li>"); } } return sb.toString(); }