版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、简易而又灵活的Javascript拖拽框架简易而又灵活的Javascript拖拽框架(一)一、开篇最近在做js拖拽的时候,发现了一个强大而又灵活的拖拽框架,(之前用了代码混淆器,还好代码比较短,我就翻译过来了)利用这个框架不仅能实现简单的拖动,更能轻易的实现各种复杂的拖放功能。这一篇先实现最简单的拖拽,稍微复杂的拖放将在后面的文章里写出来。二、代码先把代码贴出来Codevar Drag= "obj":null, "init":function(handl
2、e, dragBody, e) if (e = null) handle.onmousedown=Drag.start; &
3、#160;handle.root = dragBody; ifif/确保后来能够取得top值newnewnew Function(); if (e !=null)
4、0; var handle=Drag.obj=handle; e=Drag.fixe(e); varvarfunction(e) var handle=Dr
5、ag.obj=this; e=Drag.fixEvent(e); varvar/alert(left) /一般情况下 left top 在初始的时候都为0return false; ,
6、 "drag":function(e)/这里的this为document 所以拖动对象只能保存在Drag.obj里 e=Drag.fixEvent(e); var handle=Drag.obj; var mouseY=e
7、.pageY; var mouseX=e.pageX; varvar/这里的top和left是handle.root距离浏览器边框的上边距和左边距 var currentLeft,currentTop;
8、 currentLeft=left+mouseX-handle.lastMouseX; currentTop=top+(mouseY-handle.lastMouseY); /上一瞬间的上边距加上鼠标在两个瞬间移动的距离&
9、#160;得到现在的上边距/更新当前的位置 handle.lastMouseX=mouseX; handle.lastMouseY=mouseY; &
10、#160; /保存这一瞬间的鼠标值 用于下一次计算位移/调用外面对应的函数 return false; , "end":function() document.onmousemove=null;
11、160; document.onmouseup=nullnull; , "fixEvent":function(e)/格式化事件参数对象 if(typeof e="undefined")e=window.event; if(typeof e.laye
12、rX="undefined")e.layerX=e.offsetX; if(typeof e.layerY="undefined")e.layerY=e.offsetY; if(typeofif(typeofreturn e; 使用方法Drag.init(handle,dragBody);这样可以让鼠标拖
13、动元素。Handle为拖动的把手,dragBody是在拖动时需要移动的元素。 如果需要更高级的控制拖动或者拖放的话,可以给dragBody设置三个方法dragBody.onDragStart=function(left,top,mouseX,mouseY)onDrag(left,top,mouseX,mouseY)onDragEnd(left,top,mouseX,mouseY)这四个参数分别是:拖动对象的left属性、拖动对象的top属性(此时拖动对象的position为absolute)、鼠标当前的x、鼠标当前的y。在每个过程中都可以通过四个参数来更精确的控制拖动的每个过程,更主
14、要的是我们不需要去管被鼠标拖动的元素是怎么移动的,这些都在框架中已经做好了,这让拖拽看起来更整洁。框架具体是怎么运转的,其实很简单 三、原理Drag有几个成员Drag.obj用来存放鼠标正在被拖动的元素,只有在鼠标按下过后才会赋值,鼠标松开便为空了。便于在这几个方法之间都能够轻易的找到正在被拖动的元素。Drag.init主要是用来订阅handle的onmousedown事件,所以handle一旦点击,则会执行Drag.start方法。Drag.start是用来注册document的mousemove和mouseup事件的,而且会调用我们定义的dragBody.onDragStart方
15、法(这里的dragBody就是通过Drag.obj来获得的),以便处理框架以外需要处理的事情。Mousemove的时候会不断的触发Drag.drag方法,这个方法主要是控制dragBody的移动(通过前后两个瞬间的位移差来调整dragBody的位置),以及调用外部的dragBody.onDrag方法,处理框架以外的事情。Document的mouseup方法,调用外部的onDragEnd方法,释放document绑定的两个事件,以及将临时的obj置为空四、示例下载简易而又灵活的Javascript拖拽框架(二)一、开篇在上一篇中,似乎不出来这个框架有多强大。是因为我们实现的效果太简单了,还不足以
16、体现框架的强大和灵活,在这一节中,用这个灵活的框架轻易的来实现拖放! 二、原理 框架的原理在上一篇文章中介绍了,这里就不在重复,直接开始使用这个框架了首先通过Drag.init来注册拖动组件;分析:1、拖动开始的时候将dragGhost插入到鼠标拖动的那个元素,鼠标拖动的元素的position为absolutedragGhost为拖动过程中指示拖动元素的当前位置的元素2、拖动正在进行的时候根据当前鼠标的位置或者正在被鼠标拖动的元素的位置 和需要排列的各种元素来比较,以确定将dragGhost放在哪个地方。具体的分析,就是遍历拖动元素的各个兄弟元素,从第一元素起,一旦发现某个
17、元素的x和y比鼠标的x和y大的话,则将dragGhost添加到这个元素之前。3、拖动结束的时候将dragGhost替换为被拖动的元素,然后将dragGhost隐藏。 这样就让思路很清晰,不必再花精力去管元素是怎么随着鼠标走的,而且我们的却很精确的控制了整个拖放过程,所以说这个框架很强大很灵活三、代码Codefunction findPosX(obj) /辅助函数 得到元素左边与浏览器左边的边距 var curleft = 0; if&
18、#160;(obj && obj.offsetParent) while (obj.offsetParent) curleft += obj.offsetLeft; &
19、#160; obj = obj.offsetParent; else if (obj && obj.x) curleft += obj.x; return curleft;function findPosY(obj) /辅助函数 得
20、到元素上边与浏览器上边的边距 var curtop = 0; if (obj && obj.offsetParent) while (obj.offsetParent)
21、160;curtop += obj.offsetTop; obj = obj.offsetParent; else if (obj && obj.y) curtop += obj
22、.y; return curtop;var dragGhost;var container;window.onload = function() dragGhost = document.getElementById("dragGhost"); container = document.getElementById("container&q
23、uot;); var nodes = container.getElementsByTagName("li"); for(var i=0;i<nodes.length;i+) var li = nodesi; if(li.i
24、d != "dragGhost") new dragItem(li); var isIE = document.all;function dragItem(item) Drag.i
25、nit(item,item); item.onDragStart = function(left,top,mouseX,mouseY) this.style.left = findPosX(this); this.style.top = findPosY(this); &
26、#160; this.style.position = "absolute" /将ghost插入到当前位置this.offsetWidth:this.offsetWidth - 2;/边框问题this.offsetHeight:this.offsetHeight - 2;
27、 this.parentNode.insertBefore(dragGhost,this); item.onDrag = function(left,top,mouseX,mouseY) var nodes = container.getElementsByTagName(&qu
28、ot;li"); var width = this.offsetWidth; var height = this.offsetHeight; for(v
29、ar i=0;i<nodes.length + 1;i+) if(nodesi = null) container.appendChild(dragGhost);/拖动最后一项
30、 break; if(nodesi.id = "dragGhost" | nodesi =
31、 this) continue; if(mouseX < findPosX(nodesi
32、) + width && mouseY < findPosY(nodesi) + height) container.insertBefore(dragGhost,nodesi
33、); break; item.onDragEnd = f
34、unction(left,top,mouseX,mouseY) container.insertBefore(this,dragGhost); this.style.position = "static" this 简易而又灵活的Javascript拖拽框架(三)一、
35、开篇 这是这个拖拽系列的第三篇了,第一篇简单的介绍了一下这个拖拽框架,第二篇用这个框架做了一个Tab标签的拖放。这次用这个拖拽框架做一个更复杂一点的效果跨列拖放。就像iGoogle和netvibes的个性页面布局那样。 二、原理 框架在第一篇做了介绍,这里直接使用。首先要找到每个可以拖动的item,对于每个拖动的item,对其注册组件Drag.init(handle,dragBody);并且要把这个对象所在的列赋值给这个对象还是分三个步骤说这个拖放的过程 1、鼠标拖动开始的时候,除了要将dragGhost放到当前被拖动的对象原
36、来的位置上,还要记录每一列的左边距,当然还是要设置被拖动对象的position 2、鼠标拖动的过程中先找到拖动对象是在哪一列,通过现在拖动对象的位置和在拖动开始时记录的列的左边距相比较,得出当前拖动对象在哪一列。 如果在原来那一列上,则不用管了,如果在别的列上,则将dragGhost插入到这个新的列中去(用column.appendChild插入到最后)。无论是本来就在这一列的元素还是从别的列拖过来的元素,都得让拖动对象与他所在的列的每一个元素的纵坐标相比较,得出应该在这一列的什么位置,将dragGhost插入(用column.insertBefore)。至于怎么找到拖动元
37、素应该放在这一列的什么位置,这个原理和上边找列的原理差不多,只是比较的是纵坐标的值,比较的对象是本列的所有对象(注意,遍历的时候一定要排除dragGhost和本身,要不然在本列不能向下拖动)。 3、拖动完成将ghost替换为被拖动的元素,设置相应的样式三、代码Code/-Utility-function findPosX(obj) /辅助函数 得到元素左边与浏览器左边的边距 var curleft = 0; if (obj
38、&& obj.offsetParent) while (obj.offsetParent) curleft += obj.offsetLeft;
39、0;obj = obj.offsetParent; else if (obj && obj.x) curleft += obj.x; return curleft;function findPosY(obj) /辅助函数 得到元素上边与浏览器上边的边距&
40、#160; var curtop = 0; if (obj && obj.offsetParent) while (obj.offsetParent) curtop
41、;+= obj.offsetTop; obj = obj.offsetParent; else if (obj && obj.y) curtop += obj.y;
42、 return curtop;varvar container;var columns = /-Start Here-window.onload = function() container = document.getElementById("container"); for(varif(container.chi
43、ldNodesi.className = "column")/筛选出所有的列 ff下的childNodes不可靠 : columns.push(container.childNodesi); for(var&
44、#160;i=0;i<columns.length;i+) var column = columnsi; for(varvar item = column.childNodesj; if(item.cla
45、ssName = "item") item.column = column;/给每个拖拽对象要指明它属于哪一列 而且这个属性会随着拖动而更新的
46、60; new dragItem(item); var isIE = document.all;
47、/-Drag Item-function dragItem(item) /item实际上是dragBody(拖动的时候移动的整体) /在这里需要根据item找到handle(能够拖动的把手) var handle; for(varif(item.childNodesi.nodeName.toLowerCase() =
48、;"h3") handle = item.childNodesi; break; if
49、(!handle)return; Drag.init(handle,item); item.onDragStart = function(left,top,mouseX,mouseY) /开始拖动的时候设置透明度 &
50、#160;this.style.opacity = "0.5" thisthis.offsetHeight:this.offsetHeight - 2; /this指的是item
51、60; this.style.width = this.offsetWidth;/因为初始的width为auto this.style.left = findPosX(this) - 5; this.style.top = f
52、indPosY(this) - 5; this.style.position = "absolute" /将ghost插入到当前位置this.column.insertBefore(dragGhost,this);
53、60; /记录每一列的左边距 在拖动过程中判断拖动对象所在的列会用到 this.columnsX = for(var i=0;i<columns.length;i+)
54、0; this.columnsX.push(findPosX(columnsi); item.onDrag = function(left,top,mouseX,m
55、ouseY) /先要判断在哪一列移动 var columnIndex = 0; for(var i=0;i<this
56、.columnsX.length;i+) if(left + this.offsetWidth/2) > this.columnsXi) columnIndex = i;
57、0; /如果columnIndex在循环中没有被赋值 则表示当前拖动对象在第一列的左边 /此时也把它放到第一列
58、160; var column = columnscolumnIndex; if(this.column != column)
59、0;/之前拖动对象不在这个列 /将ghost放置到这一列的最下方 column.appendChild(dragGhost);
60、; this.column = column; /然后在判断放在这一列的什么位置
61、 var currentNode = null; for(var i=0;i<thisif(this.column.childNodesi.className = "item" &&
62、;this.column.childNodesi != this/不能跟拖动元素自己比较 否则不能在本列向下移动 && top < findPosY(this.column.childNodesi)/从上到下找到第一个比拖动元素的上边距大的元素
63、 currentNode = this.column.childNodesi; break;
64、 if(currentNode) this.column.insertBefore(dragGhost,currentNode);
65、60;else/拖到最下边 没有任何一个元素的上边距比拖动元素的top大 则添加到列的最后 this.column.appendChild(dragGhost); item.onDragEnd = function(left,t
66、op,mouseX,mouseY) this.style.opacity = "1" this.style.filter = "alpha(opacity=100)"
67、; this.column.insertBefore(this,dragGhost); this.style.position = "static" this.style.display = "block"
68、0; this 如果这样不方便观看,可以下载示例。四、示例下载简易而又灵活的Javascript拖拽框架(四)一、开篇似乎拖拽已经被写烂了,没得写的了,可是我这次又来了上一次写的是跨列拖放,这次我要带给大家的是跨页拖放。可以到这里来看看效果:示例效果说明:1、如果将方框拖动到页签上立刻释放掉的话,则会被添加到该页的第一列的第一个位置; 2、如果将方框拖动到页签上并且停留片刻的话,则页面就会转换到该页,这个时候可以在页签上释放,也可以将方框拖动到此页的具体位
69、置释放。二、原理 我是在跨列拖放的基础上修改的代码,虽然仅仅是从跨“列”升级为跨“页“,但是这就意味着多了一个dimension。所以代码改动比较大,尤其是初始化的代码。为了弄清楚代码中的命名同时也便于阐述原理,我画了下面的图1、在拖动开始时,跟之前的跨列拖拽差不多,基本上不需要修改;2、在拖动的过程中,就需要判断拖动module时的鼠标是否是在某个tab上,如果在tab上,则把dragGhost(拖动中占位的虚线框)隐藏了,并且设置转换页面的timeout(注意:这个timeout不要设置重复了,而且如果鼠标就在本页上就不需要设置),在设置这个
70、timeout的响应函数也要小心,必须先把拖动的module放到新的页面然后再转换页面,因为鼠标虽然拖动了module,但是在html代码中,这个module还是属于原来的页面(只是因为position为absolute才让它游离出来的),如果原来的页面因为页面转换而变得不可见了,那么鼠标拖动的module也会不翼而飞的。3、在拖动的过程中,如果鼠标不在某个tab上,首先要将timeout及时清除了,要不然在拖动时会莫名其妙的转换页面,剩下的跟页面内拖放是一样的计算方法来处理,也是先计算所在的列,然后计算再这个列的位置,在此不再累述。4、在拖动的过程中,注意维持module的column变量,
71、这个变量对于拖放很重要,要及时而正确的更新。5、在拖动结束的时候,如果鼠标还在某个tab上(无论这个时候页面是不是因为鼠标的停留而改变),则把module放在这一页的第一列的第一个位置。如果不在tab上,那么和页面内拖放是一样的。无论怎样,在最后都要设置一些style以及更新个别变量,放置完毕。三、代码 原理说起来容易,写起来还是很麻烦的,而且得经过很多次测试才能成功的。 不过我总结出来几点:
72、 1、对于任何一个对象,要分清楚这个对象是html对象还是我们自定义类的对象; 2、各种对象尽量少维持一些变量,要不然在每次动作发生的时候都会去更新一堆变量,那将是很麻烦的(或许可以将这些变量的设置封装成对象的方法); 主要代码如下:Codevar module = function(moduleElm) var self = this
73、; this.elm = moduleElm; this.elm.module = this; this.column = moduleElm.column; this.page = this.column.page; this.handle = this.elm.ge
74、tElementsByTagName("h3")0; /这里只是为了各个页面的module看起来不一样 所以另外设置一下style /page的id也是为了这个目的而加的 其他地方page的id是用不上的 switch(this.page.id) case "
75、page1":thisbreak; case "page2":thisbreak; case "page3":thisbreak; case "page4":thisbreak;
76、160; if(this.handle && this.elm) Drag.init(this.handle,this.elm); else return;
77、0; this.elm.onDragStart = function(left,top,mouseX,mouseY) /开始拖动的时候设置透明度 this.style.opacity = "0.5"
78、 thisthis.offsetHeight:this.offsetHeight - 2; /this指的是item
79、 this.style.width = this.offsetWidth;/因为初始的width为auto this.style.left = findPosX(this) - 5; this.style.top = findPosY(this) - 5;
80、; this.style.position = "absolute" /将ghost插入到当前位置this); /记录每一列的左边距&
81、#160;在拖动过程中判断拖动对象所在的列会用到 this.columnsX = for(varthisthis.elm.onDrag = function(left,top,mouseX,mouseY)
82、; this.currentTab = null; /判断是否在tab上 for(varvar tabElm = XDrag.tabsi.elm; &
83、#160; if(findPosX(tabElm) < mouseX) && (findPosX(tabElm) + tabElm.offsetWidth > mouseX) &&
84、 (findPosY(tabElm) < mouseY) && (findPosY(tabElm) + tabElm.offsetHeight > mouseY)
85、0; this.currentTab = XDrag.tabsi; break;
86、 if(this.currentTab != null) iffunction changeTab()
87、160; /先得把module放到当前的这一页 /否则会随着tab的改变而消失
88、; varvar flag = false; for(varif(currentColumn.childNodesi.nodeName.toLowerCase() = "div")
89、60; currentColumn.insertBefore(self.elm,currentColumn.childNodesi);
90、 flag = true; break;
91、160; if(!flag)
92、; currentColumn.appendChild(this); self.column = currentColumn;/将拖动的module添加到这一页的第一列 因为display还为absolute 所以module还跟着鼠标在走null; &
93、#160; /如果Timeout不为空(防止重复设置Timeout) 而且移动到的tab不是当前的tab(如果是当前tab则不需要改变tab页了) &
94、#160; if(XDrag.changeTabTimeoutId = null && this.currentTab != XDrag.selectedTab)
95、160;XDrag.changeTabTimeoutId = setTimeout(changeTab,XDrag.changeTabTimeout); return;/如果鼠标在tab上 则不必理会页面内的移动了
96、; /以下是计算在页面内拖拽的代码 clearTimeout(XDrag.changeTabTimeoutId);
97、; XDrag.changeTabTimeoutId = null;/既然鼠标都没有在tab上了 当然就应该清空timeout了 /先要判断在哪一列移动 var columnIndex = 0;
98、160; for(var i=0;i<this.columnsX.length;i+) if(left + this.offsetWidth/2) > this.columnsXi)
99、 columnIndex = i; /如果columnIndex在循环中没有被赋值 则表示当前拖动对象在第一列的左边&
100、#160; /此时也把它放到第一列 varif(self.column != column) /之前拖动对象不在这个列
101、 /将ghost放置到这一列的最下方 /如果已经跨页拖放了 也会执行这里的
102、160; column.appendChild(dragGhost); self.column = column; &
103、#160; /然后在判断放在这一列的什么位置 var currentNode = null; for(varifthis/不能跟拖动元素自己比较 否则不能在本列向下移动/从上到下找到
104、第一个比拖动元素的上边距大的元素break; ifelse/拖到最下边 没有任何一个元素的上边距比拖动元素的top大 则添加到列的最后this.elm.onDragEnd = function(left,top,mouseX,
105、mouseY) if(this.currentTab != null) /this.currentTab != null表示鼠标拖拽的module在tab上释放 无论这个时候tab是否因为鼠标的停留而转换了页签
106、0; clearTimeout(XDrag.changeTabTimeoutId); XDrag.changeTabTimeoutId = null; &
107、#160; var firstColumn = thisvar flag = false; for(varif(firstColumn.childNodesi.nodeName.toLowerCase() = "div")
108、60; firstColumn.insertBefore(this,firstColumn.childNodesi); flag = true;
109、0; break;
110、0; if(!flag) firstColumn.appendChild(this); self.column = firstColumn; &
111、#160; elsethis,dragGhost); this.style.opacity = "1" this.style.filter = "alpha(opacity=100)"
112、60; this.style.position = "static" this.style.display = "block" this/需要手动更新(奇怪 self.
113、page难道是个值类型) /也可以不要最后这一句 仅仅是为了数据的完整性 四、示例下载简易而又灵活的Javascript拖拽框架(五)一、开篇 在写了前几篇拖拽过后,有不少朋友留言说如果将拖拽结果持久化将会更好。在了解了Javascript对Cookie的
114、操作还有一点JSON知识过后,修改了上一篇文章的代码,可以将结果保存在客户端Cookie,每次打开页面就可以载入上次拖拽的数据,得到上次的拖拽布局。 查看示例二、原理 首先添加了两个方法:setCookie和getCookie,分别用来添加/修改Cookie和读取Cookie,这些教程太多了,就不多说了。 然后来说说打开网页到关闭网页的整个过程:1、打开网页的时候,会通过getCookie去读取名为“XDrag”的Cookie,如果用户是第一
115、次打开这个页面(或者是禁用、手动删除了Cookie,总之客户端没有对应的Cookie的时候),则会有一个默认的配置;2、网页的html元素的结构也有所变化,以前是直接把拖拽的模块直接放置在对应的页对应的列,现在是统一放在tempContainer这个div中,通过读取的配置来将tempContianer里面的模块“分发”到配置指定的页制定的列;3、注册window.onbeforeunload事件,在页面unload之前保存配置到Cookie。保存的方法就是遍历每一页的每一列的每一个模块,将每个模块的id记录下来,根据页面和列和id来构造一个JSON字符串(因为Cookie保存的值就是字符串)
116、,这样读取的时候只需要用eval即可得到保存的数据的对象,这是JSON的优点。下面是这个JSON字符串的结构,嵌套了3层array jsonObj.pages得到pages数组jsonObj.pages0得到page1的列的数组jsonObj.pages00得到page1的第一列的所有id的集合三、代码 下面是几个添加的几个主要的方法:setCookie和getCookiefunction setCookie(sName,sValue,oExpires,sPath,sDomain,bSecure)
117、60; var sCookie = sName + "=" + encodeURIComponent(sValue); if(oExpires) sCookie += " expires=" + oExpires.toGMTString();
118、 if(sPath) sCookie += " path=" + sPath; if(sDomain) sCookie += " domain=" + sDomain;
119、 if(bSecure) sCookie += " secure" document.cookie = sCookie;function getCookie(sName) var sRE = "(?:; )?" + sName + "=(;*);?"&
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- T/CSTEA 00010-2020九龙大白茶 白茶
- T/ZFIDA 0002-2024面向网络攻击的银行安全能力评估指南
- T/HEESA 0002-2025电力信息系统网络安全等级保护测评报告评审指南
- T/CIECCPA 020-2024二氧化碳捕集、运输与封存工艺技术规范
- T/CAPS 024-2025无人机电池性能检测设备技术要求
- T/CSBM 0050.3-2024创面修复材料有效性评价 第3部分:刺激因子屏蔽评价方法
- 2025~2026学年辽宁大连市沙河口区北师大版五年级上册数学阶段学情自测卷【附答案】
- 2025~2026学年广东省广州市从化区人教版六年级上册自主练习二数学试卷【附答案】
- 2025-2026学年湖南省张家界市初中历史八年级期末下册评估压轴试题(详细参考解析)
- PHP+HTML高频面试题及详细答案(真实职场版)
- 2026年继续教育公需课考试试题及答案
- 2026年浙江省嘉兴市国家职业技能鉴定考评员试卷含参考答案
- 2026-2030中国羧甲基纤维素市场供需调查及竞争格局研究报告
- 骆驼基因组解析:探索其癌症相关基因的奥秘与启示
- 2026-2030中国咖啡生豆行业竞争格局及消费趋势预测报告
- 2026年江西中考物理试题【含答案解析】
- 2026年河北工人技师考评(环境噪声监测工)自测试题及答案
- DB32-T 5388-2026 电动车充电基础设施建设技术规程 第2部分:电动汽车
- (高清版)JTG 2112-2021 城镇化地区公路工程技术标准
- 飞飞自动打怪脚本
- 盐湖资源开发环境影响评价
评论
0/150
提交评论