[转载]工欲善其事必先利其器—SQL在线可视化模型设计,(还可学习拖拽知识) – 沫尘 – 博客园.
作为技术人员,在开发项目中,不可避免的要跟数据库打交道,一个完整的项目正常情况下是讨论完整体需求,有了大致的框框在脑海中后,是需要设计合理的数据 库的,这时会有其他的专业的UML建模工具可以使用, 但是这种大型的专业化的UML建模工具,仅仅拿来建立数据库模型显得大材小用了,并且也太重量级,所以一般都不会轻易使用。多数人会选择直接在数据库软件 中直接鼠标操作建表、填充列, 这样更是不方便, 故而很多人提到了VS中的codefirst方式, 直接在代码中建立数据库模型, 这种方式生成数据库之后,如果回头维护、查看都不太友好,甚至过了几个月之后你自己回头看都有可能不知道是什么意思。 这样我们就需要数据库说明。
常见的数据库说明都是word文档,类似如下:
这是很多公司或团队采用的, 但是我觉得这样还是不爽,假定你是一个接手人, 你拿着这样的表格,左边点开数据库,右边打开这个文档,瞄一眼数据库再瞄一眼这个表格,左右切换窗口,超级不爽啊。
另外一种更加直观的,正向思维的方式:
这样看起来是不是直观多了, 清楚明白的知道这个表的构成,直接在数据库中打开,不用切换窗口,它是建表脚本,也是一份详细直观的数据库说明文档, 后续对数据库进行修改、增加、删除结构都很直接、直观。
于是,我个人就弄了个自己用的方便的可视化的数据库设计的东西,当然最终是生成上述直观可读性高的脚本文件,今天把它写出来分享下。
我的环境是mvc3, 其实这个东西根本用不着MVC, 很简单,就一个页面大量的JS操作,提交到后台代码生成文件下载回本地。先看打开时:
默认加载一个空表,填写相应的名称之类的,字段、类型、默认值、备注信息等,+号增加一行,如果一行没有完成会提示:
点击新建表会增加一个表,整个筐筐内每个表模型可以任意拖动位置且高度会自适应。拖拽都是靠JS。整个界面代码如下:
@{ ViewBag.Title = "主页" ; } <div class = "box" > <div class = "editmenu" > <input id= "txtBaseName" type= "text" value= "@ViewBag.baseName" /> <input id= "txtBaseDes" type= "text" value= "@ViewBag.baseDes" /> <input id= "addTable" type= "button" value= "新建表" /> <input id= "getData" style= "float: right;" title= "下载完整的TXT格式的数据库脚本" type= "button" value= "下载数据" /> </div> <div class = "main" id= "main" > <div class = "tbbox" > <div class = "titletr" title= "" ><span class = "spleft" > <input class = "tbname" type= "text" value= "XXX表" /> </span> </div> <div class = "titletr" title= "" ><span class = "spleft" > 名:<input class = "tbname" type= "text" value= "tb_1" /> </span> <span class = "spright" >删除</span></div> <table class = "tablelist" > <tbody> <tr> <td>字段名</td> <td>类型</td> <td>默认值</td> <td>备注</td> <td style= "border: 0;" ></td> </tr> <tr> <td><input type= "text" /></td> <td> <div class = "sortdiv" ><span class = "selspan" ><span class = "selspan" ><select class = "selsort" ><option value= "int" >int</option><option value= "varchar(50)" >varchar(50)</option><option value= "nvarchar(200)" >nvarchar(200)</option><option value= "datetime" >datetime</option><option value= "float" >float</option><option value= "text" >text</option><option value= "ntext" >ntext</option></select></span></span> <input class = "sortinput" type= "text" value= "int" /></div></td> <td><input class = "txtdef" type= "text" /></td> <td><input type= "text" /></td> <td class = "jiatr" id= "tool" style= "border: 0;" >+</td> </tr> </tbody> </table> </div> </div> </div> <input id= "hdbase" type= "hidden" value= "@ViewBag.baseId" /> <input id= "hdtr" type= "hidden" value= "@ViewBag.Tr" /> <input id= "hddiv" type= "hidden" value= "@ViewBag.div" /> <input id= "hdall" type= "hidden" value= "@ViewBag.AllData" /> <script type= "text/javascript" > // <![CDATA[ $(document).ready( function () { window.onload = function () { if ($( "#hdall" ).val() != "" ) { $( "#main" ).html($( "#hdall" ).val()); } ///初始化给div加移动事件 $( "#main" ).find( "span.title" ).each( function () { this .onmousedown = MoveBegin; }); ///给+TD事件 $( "#main" ).find( "td.jiatr" ).each( function () { $( this ).click(TdClick); $( this ).attr( "title" , "新增一行" ); }); ///添加DIV按钮 $( "#addTable" ).click(AddTbale); $( "#getData" ).click(GetData); $( "#saveData" ).click(SaveData); ///给select绑定事件 $( "#main" ).find( "div.tbbox" ).each( function () { SortList( this ); $( this ).find( "span.spright" ).click(DelDiv); }); CheckMainHeight(); } ///返回txt文件 function SaveTxt(arr) { var loogParam = arr; var nwin = window.open( '/home/DataText?t=' + Math.random(), 'newwindow' , 'height=5, width=5, top=400,left=500, toolbar=no, menubar=no, scrollbars=no, resizable=no,location=no, status=no' ); document.body.insertAdjacentHTML( "beforeEnd" , " <form name='form1' action='/home/DataText' target='newpage' method='post'><input type='hidden' name='param'></form> " ); document.form1.param.value = loogParam; document.form1.submit(); $( "form" ).remove(); nwin.close(); } ///txt文件空格占位 function NullSpace(defnum, str) { var s = "\040" ; var r = "" ; var l = str.length; var num = defnum - l; for ( var i = 0; i < num; i++) { r += s; } return r; } ///"-"占位符 function NullH(defnum, str) { var s = "-" ; var r = "" ; var l = str.length; var num = defnum - l; for ( var i = 0; i < num; i++) { r += s; } return r; } ///下载脚本为txt文件 function GetData() { var tbName = ""; var tr = 0; var sav = true; var br = "\r\n"; var strSql = "use " + $("#txtBaseName").val() + br; $("#main").find("div.tbbox").each(function () { tbName = $($(this).find(".tbname")[1]).val(); var desName = $($(this).find(".tbname")[0]).val(); var sqlExists = "if exists ( select * from sysobjects where name = '" + tbName + "' and xtype='U') " + br + "" + "drop table " + tbName + ""; strSql += sqlExists + br; strSql += "create table " + tbName + NullH(67, tbName) + desName + br + "(" + br; $(this).find("tr").each(function (index, element) { if (index > 0) { var tdlist = $( this ).find( "td" ); var key = $(tdlist[0]).find( "input" ).val(); if ($.trim(key) != "" ) { if (key.length >= 17) { alert(key + "超出了15个字符的限制" ); //alert(strSql); sav = false ; return ; } key += NullSpace(17, key); var col = $(tdlist[1]).find( "input" ).val(); col += NullSpace(17, col); if (index == 1) col = "int" + NullSpace(17, "int" ); var def = $(tdlist[2]).find( "input" ).val() == "" ? "default ''" : "default " + $(tdlist[2]).find( "input" ).val(); def += NullSpace(32, def); if (index == 1) def = "primary key identity(1,1)" + NullSpace(32, "primary key identity(1,1)" ); var des = "--" + $(tdlist[3]).find( "input" ).val(); if (index == 1) des = "--主键-" + $(tdlist[3]).find( "input" ).val(); strSql += "\040\040" + key + col + def + "NOT NULL , " + des + br; tr++; } } }); strSql += ");" + br + br + br; }); if (sav && tr > 0) { $( "#saveData" ).click(); SaveTxt(strSql); } } ///给数据类型列加事件 function SortList(obj) { $(obj).find( "div.sortdiv" ).each( function () { $( this ).find( "select:first" ).change(ChangeVal).change(DefaultTxt); $( this ).find( "input:first" ).focus(ShowSel); $( this .parentNode).mouseenter(ShowSel).mouseleave(HideSel); $( this .parentNode.parentNode).find( ".txtdef" ).blur(DefaultTxt); }); } ///删除div function DelDiv() { $( this .parentNode.parentNode).remove(); } ///改变数据类型 function ChangeVal(obj) { $( this .parentNode.parentNode).find( ".sortinput" ).val($( this .parentNode).find( ".selsort" ).val()); } ///默认值 function DefaultTxt(obj) { var txt = $( this .parentNode.parentNode).find( ".txtdef:first" )[0]; if (obj.type == "change" ) { txt = $( this .parentNode.parentNode.parentNode.parentNode).find( ".txtdef:first" )[0]; } var val = $(txt).val().replace( "'" , "" ).replace( "'" , "" ); $(txt).val(val); if ($.trim($(txt).val()) != "" && $.trim($(txt).val().toLowerCase()) != "getdate()" && $.trim($(txt.parentNode.parentNode).find( ".sortinput" ).val()) != "int" && $.trim($(txt.parentNode.parentNode).find( ".sortinput" ).val()) != "float" ) { $(txt).val( "'" + val + "'" ); } } ///数据类型下拉显示 function ShowSel(obj) { $( this .parentNode).find( ".selspan" ).show(); $( this .parentNode).find( ".sortinput" ).width( "90px" ); $( this .parentNode).find( ".sortinput" ).css( "position" , "absolute" ); } ///数据类型下拉隐藏 function HideSel(obj) { var td = this ; var o = obj.relatedTarget || obj.toElement; if (!o) { return ; } $(td).find( ".selspan" ).hide(); $(td).find( ".sortinput" ).width( "118px" ); $(td).find( ".sortinput" ).css( "position" , "" ); } ///添加DIV function AddTbale() { var child = document.createElement( "div" ); child.className = "tbbox" ; var name = Math.random().toString().substr(2, 3); var div = $( "#hddiv" ).val().replace( "tb_Name" , "tb_" + name); $(child).html(div); $(child).find( "span.title" ).each( function () { this .onmousedown = MoveBegin; }); $(child).find( "span.spright" ).click(DelDiv); $(child).find( "td.jiatr" ).each( function () { $( this ).click(TdClick); $( this ).attr( "title" , "新增一行" ); }); SortList(child); $( "#main" ).append(child); CheckMainHeight(); } ///TD增加一行点击 function TdClick(obj) { var tooltipem = $( this .parentNode).find( "td:first" ).find( "input:first" ); if ($.trim(tooltipem.val()) == "" ) { $(tooltipem).tooltip({ "title" : "这里还没填写呢~!" , "trigger" : "manual" , "placement" : "right" , "delay" : 800 }); $(tooltipem).tooltip( "show" ); $(tooltipem).focus(); setTimeout( function () { $(tooltipem).tooltip( "hide" ); }, 1800) return false ; } $(tooltipem).tooltip( "destroy" ); var tr = $( "#hdtr" ).val(); var child = document.createElement( "tr" ); $(child).html(tr); $( this .parentNode.parentNode).append(child); SortList(child); $(child).find( "td.jiatr" ).click(TdClick); $( this ).removeClass(); $( this ).unbind(); $( this ).html( "" ); $( this ).attr( "title" , "" ); CheckMainHeight(); $($($( this .parentNode.parentNode).find( "tr:first" ).next( "tr" ))).find( "td:last" ).addClass( "prmkey" ).attr( "title" , "第一行默认为主键&自增长" ); } ///校验div#main高度 function CheckMainHeight() { $( "#main" ).find( "div" ).each( function () { if ( this .offsetTop + $( this ).height() > $( "#main" ).height()) $( "#main" ).height( this .offsetTop + $( this ).height() + 10); }); } ///移动 function MoveBegin(obj) { var e = obj || window.event, x1 = e.clientX; y1 = e.clientY; var setOn = this .parentNode.parentNode; var defLeft = setOn.offsetLeft; var defTop = setOn.offsetTop; document.onmousemove = function (event) { var e = event || window.event; // x2 = e.clientX; y2 = e.clientY; // x = x2 - x1; y = y2 - y1; var setLeft = defLeft + x; var setTop = defTop + y; setOn.style.position = "absolute" ; //setOn.style.filter = 'alpha(opacity=80)'; //setOn.style.opacity = "0.8"; if (setLeft <= 0) { setLeft = 0; } if (setTop <= 0) { setTop = 0; } if (setLeft >= $( "#main" ).width() - $(setOn).width() - 9) { setLeft = $( "#main" ).width() - $(setOn).width() - 14; } if (setTop > $( "#main" ).height() - $(setOn).height() + 10) { $( "#main" ).height(setTop + $(setOn).height() + 100); setTop = $( "#main" ).height() - $(setOn).height(); } setOn.style.left = setLeft + 'px' ; setOn.style.top = setTop + 'px' ; //$("#showText").text("Top:" + setOn.style.top + "; Left:" + setOn.style.left + "; OFl:" + ""); } document.onmouseup = function () { this .onmousemove = null ; } } }); // ]]></script> |
里面有JS拖拽代码, 都很简单, 上面这些是我2年前做的东西,很多命名之类的都不规范,大家关注方法就好。
后台代码:
public ActionResult Index() { StringBuilder sb = new StringBuilder(); sb.Append( "" ); sb.Append( "<input type=" \"text\" " />" ); sb.Append(" "); sb.Append( "" ); sb.Append(" <div class = "\"sortdiv\"" >"); sb.Append( "<span class=" \"selspan\" "><span class=" \"selspan\" ">" ); sb.Append(" < select class = "\"selsort\"" >");</ select > < select class = "\"selsort\"" >sb.Append("</ select > < select class = "\"selsort\"" ><option value= "\"int\"" > int </option></ select ></span></span>"); sb.Append(" varchar(50) "); sb.Append(" nvarchar(200) "); sb.Append(" datetime "); sb.Append(" float "); sb.Append(" text "); sb.Append(" ntext "); sb.Append(" "); sb.Append(" "); sb.Append( "<input class=" \"sortinput\" " type=" text " value=" \" int \" " />" ); sb.Append("</div> "); sb.Append(" "); sb.Append( "" ); sb.Append( "<input class=" \"txtdef\" " type=" \"text\" " />" ); sb.Append(" "); sb.Append( "" ); sb.Append( "<input type=" \"text\" " />" ); sb.Append(" "); sb.Append(" "); sb.Append( "+" ); sb.Append(" "); ViewBag.Tr = sb.ToString(); StringBuilder sbdiv = new StringBuilder(); sbdiv.Append(" <div class = "titletr" title= "" >"); sbdiv.Append( "<span class=" spleft ">" ); sbdiv.Append( "<input class=" tbname " type=" text " value=" XXX表 " />" ); sbdiv.Append( "</span>" ); sbdiv.Append( "" ); sbdiv.Append( "" ); sbdiv.Append("</div> "); sbdiv.Append(" <div class = "titletr" title= "" >"); sbdiv.Append( "<span class=" spleft ">" ); sbdiv.Append( "名:<input class=" tbname " type=" text " value=" tb_Name " />" ); sbdiv.Append( "</span>" ); sbdiv.Append( "" ); sbdiv.Append( "<span class=" spright ">删除</span>" ); sbdiv.Append("</div> "); sbdiv.Append(" "); sbdiv.Append(" "); sbdiv.Append(" "); sbdiv.Append(" "); sbdiv.Append( "" ); sbdiv.Append( "类型" ); sbdiv.Append( "" ); sbdiv.Append( "" ); sbdiv.Append( "默认值" ); sbdiv.Append( "" ); sbdiv.Append( "" ); sbdiv.Append( " 备注" ); sbdiv.Append( "" ); sbdiv.Append(" "); sbdiv.Append( "" ); sbdiv.Append( "" ); sbdiv.Append(" "); sbdiv.Append(" "); sbdiv.Append( "" ); sbdiv.Append(" <div class = "sortdiv" > <div class = "sortdiv" >"); sbdiv.Append( "<span class=" selspan "><span class=" selspan ">" ); sbdiv.Append(" < select class = "selsort" >");</ select > < select class = "selsort" >sbdiv.Append("</ select > < select class = "selsort" ><option value= "int" > int </option></ select ></span></span>"); sbdiv.Append(" </div> </div> varchar(50) <div class = "sortdiv" > <div class = "sortdiv" > "); sbdiv.Append(" </div> </div> nvarchar(200) <div class = "sortdiv" > <div class = "sortdiv" > "); sbdiv.Append(" </div> </div> datetime <div class = "sortdiv" > <div class = "sortdiv" > "); sbdiv.Append(" </div> </div> float <div class = "sortdiv" > <div class = "sortdiv" > "); sbdiv.Append(" </div> </div> text <div class = "sortdiv" > <div class = "sortdiv" > "); sbdiv.Append(" </div> </div> ntext <div class = "sortdiv" > "); sbdiv.Append(" "); sbdiv.Append(" "); sbdiv.Append( "<input class=" sortinput " type=" text " value=" int " />" ); sbdiv.Append("</div> "); sbdiv.Append(" "); sbdiv.Append( "" ); sbdiv.Append( "<input class=" txtdef " type=" text " />" ); sbdiv.Append( "" ); sbdiv.Append( "" ); sbdiv.Append( "<input type=" text " />" ); sbdiv.Append( "" ); sbdiv.Append(" "); sbdiv.Append( " +" ); sbdiv.Append( "" ); sbdiv.Append( "" ); sbdiv.Append( "" ); sbdiv.Append(" <table class = "tablelist" > <tbody> <tr> <td>"); sbdiv.Append( "字段名" ); sbdiv.Append("</td> </tr> <tr> <td>"); sbdiv.Append( "<input type=" text " />" ); sbdiv.Append("</td> </tr> </tbody> </table> "); ViewBag.div = sbdiv.ToString(); ViewBag.AllData = "" ; ViewBag.baseId = "" ; ViewBag.baseName = "basename" ; ViewBag.baseDes = "备注说明" ; return View(); } //响应下载脚本请求,返回txt文件 public void DataText() { if (Request[ "param" ] == null ) return ; string t = Request[ "param" ]; if (t == "" ) return ; string tbName = "数据库-" + System.DateTime.Now.ToString( "yyMMdd-HHmmss" )+ ".sql" ; Response.AppendHeader( "Content-Disposition" , "attachment;filename=" + tbName); Response.ContentEncoding = System.Text.Encoding.Default; Response.ContentType = "text/plain" ; Response.Write(t); } |
没时间分段说了, 有源码和在线暂时测试地址:http://websql.puworld.com