一、前言
作为一个码农这么多年,一直在想怎么提高我们的编码效率,关于如何提高编码效率,我自己的几点体会
1、清晰的项目结构,要编写代码的地方集中
2、实现相同功能的代码量少并且清晰易懂
3、重复或有规律的代码应该自动生成
在这里我就讨论下代码生成的问题。
二、关于代码生成器
刚毕业时我也非常迷信代码生成器,喜欢在网上找一些代码生成器及相关的源码,喜欢在和网友讨论哪款生成器***用,但实际上很少真正用这些东西来开发项目,原因很简单:
1、生成出来的代码不是我们要的代码
2、生成后的代码再修修改改,其实还没有我的ctrl+c和ctrl+v速度快。
3、生成的基本上是实体类及sql拼接代码,现在直接用linq或一些好用的orm多方便,谁还用SQLHelper加sql文拼接?
4、b/s项目中没有一个生成器能很好的能生成UI层代码及前端交互的js代码,即使能生成也是简单的页面。
所以,我劝大家不要迷信代码生成器了。它的确可以提高我们的效率,但是并不是网上你找一个生成器就行的。代码生成器它只是一个模板引擎而已,最重要的不是代码生成器本身,而是对一类功能或一类页面的代码规范,对自己代码的提炼,提炼出一个通用的模板。
比如我们常见的查询页面,录入页面等,我们只要提炼一个标准的查询页面的代码,包括前台html,前台js,后台控制器,后台数据服务。然后把它写成模板,再利用模板引擎就可以生成我们需要的代码了。
代码生成器本身就是模板引擎,所以我觉得***的代码生成器不是网上流传的那些可以生成三层架构代码的软件,而是微软的razor引擎,非常简洁易懂,而且做过asp.net mvc项目的朋友应该也很熟悉。我个人觉得这是用来做代码生成***的引擎。
三、页面模板
我们还是会想要快速开发,比如我选择了一些设定之后,就可以直接生成我想要的代码包括html及js,拷贝到项目中就可以直接运行,运行后就看到我想要的页面,基本功能都有。当然这里所说的快速开发是建立在我对页面功能的提炼模板之上的。实际上我提炼了三种模板:
1、查询页面
这个模板可以解决大部分的查询报表业务功能
2、编辑页面
这个编辑模板可以解决基本上所有的录入功能,因为包括了主表,及多个从表(1:N或1:1)录入,而且可以一次性在同一事务中保存。并且定义了很多触发前后事件用于写一些业务处理,并且做到差异更新。
3、查询编辑页面,可以查询也可以直接在grid中编辑,这个页面用于做一些简单单据或一些基础数据页面。
四、代码生成原理
把以上页面代码做成razor模板,razor模板 + 设定选项 ==razor引擎==> 页面代码
怎么利用razor引擎,其实有以下几种方法:
1、直接利用mvc的view输出功能,以下为关键代码
- var stringWriter = new StringWriter();
- var viewContext = new ViewContext(controllerContext, view, viewData, TempData, stringWriter);
- view.Render(viewContext, stringWriter);
- var result = stringWriter.ToString();
用这种方法的优点在于不需要引入第三方类库,直接调用mvc视图的Render方法生成,而且效率很高,缺点是controllerContext及view对象的构建获取非常复杂。这种方法适用于有洁辟的码农们,我属于这一种。
2、利用第三方类库RazorEngine输出,以下为关键代码
- var template = "Hello @Model.Name! Welcome to Razor!";
- var viewData = new { Name = "World" });
- var result = Razor.Parse(template, viewData);
这代码很清爽,一目了然,只不过要引入RazorEngine类库,而且效率不如前者。
五、代码生成用户界面
我们模板准备好了,引擎准备好了,那么还需要一个数据输入viewData,我们做用户界面的目的也就是为了更好的定义这个viewData。
这个用户界面我们还是要把三种页面的定义分开:
1、查询页面生成
***步,选择代码类别search(查询页面),选择数据库,选择业务主表,再勾选字段即可实现查询条件部的设置,并且实现了拖拉排序功能。大家可以对照查询模板看。
第二步,选择grid中要显示的列,并且设置属性,格式化等
第三步,设置一些全局设定,主要根据这些参数确定命名空间,生成文件名等信息
点击生成按钮,按设定生成代码,生成后弹出文件夹,已分别生成MVC三层代码
mms_receive.cs
Index.cshtml
ReceiveController.cs
把这个代码直接拷贝到项目中直接运行,测试条件过滤都没有问题,grid会自适应高度,grid远程排序,选择分页翻页都没有问题,所有的功能都可用,
只有lookup控件弹出是空值,因为只把控制设置为了lookup但没有为它设置更详细的选项。autocomplete也是同样
即代码生成器已经生成了一个大的结构及UI,一些小细节还是要手动修改下,代码生成的UI界面如果把每个控件的选项也做进去会相当的复杂,也没有必要再细化了。
2、编辑页面生成
***步,选择主表编辑区的字段及控件类型,控件类型中的高级还未实现,这个编辑的UI也可以参照编辑的模板看
第二步,添加tab页签,选择页签类型(grid,form,empty) grid是指跟主表N:1关系,form是指跟主表1:1关系,empty是空页签,生成后自己可以添加内容
这里我随便添加三个tab页签tab1 tab2 tab3
tab1用来放人员变动grid(跟主表关系N:1)
tab2就选择form(跟主表关系1:1,也可以是主表本身)
tab3也随便添些东西
第三步,其它设置
点击生成按钮,生成后自动打开文件夹
把这些代码拷贝到项目中直接运行
tab2
tab3,修改主表数据,tab1,tab2,tab3点保存,能保存成功,
审核按钮也可用,审核后单据不可修改
这个编辑功能基本上可以囊括很多的录入页面了,可以算是比较通用了
3、查询编辑页面(查询编辑在同一个页面内)页面生成
***步,选择查询条件并设置控件类型
第二步,设置grid中的数据列,及编辑器
第三步,其它设置
点击生成按钮,生成后自动打开文件夹
把代码直接拷贝到项目中运行,结果如下,经测试除了控件还需要进一步设置,所有按钮功能正常使用
#p#
六、代码生成页面的源码
Index.cshtml
- @{
- ViewBag.Title = "代码生成";
- Layout = "~/Views/Shared/_Layout.cshtml";
- }
- @section head{
- <link href="~/Content/js/jquery-plugins/smartwizard/smart_wizard.css" rel="stylesheet" />
- <style type="text/css">
- div#navigation{float: left;width: 180px;}
- div#wrapper{float: right;width: 100%;margin-left: -185px;}
- div#wizard{margin-left: 185px;}
- ul.anchor{margin:0 0 10px 0 !important;}
- ul li{margin-left:-16px;}
- .grid .z-txt{margin:0 -3px;width:90%;}
- .grid input{width:90%;}
- .grid input[type=checkbox]{cursor:default;}
- .grid select{width:80%;padding:0 !important;height:22px;}
- .grid select + a{margin:5px;}
- .tDnD_whileDrag{background-color: #FBEC88 !important;}
- </style>
- }
- @section scripts{
- <script src="~/Content/js/jquery-plugins/smartwizard/jquery.smartWizard.js"></script>
- <script src="~/Content/js/jquery-extend/jquery.tablednd.js"></script>
- @Scripts.Render("~/Resource/Sys/Generator.js")
- <script type="text/javascript">
- $(function () {
- ko.applyBindings(new viewModel());
- });
- </script>
- }
- <div id="container">
- <div id="navigation">
- <div class="panel-header" style="width: 168px; border-width: 0; background: #FAFAFA;">
- 代码类别
- <input type="text" class="z-txt" data-bind="easyuiCombobox:codetype" />
- <div style="margin:1px;"></div>
- 数据库名
- <input type="text" class="z-txt" data-bind="easyuiCombobox:database" />
- <div style="margin:5px;"></div>
- <div data-bind="autoheight:60" style="width: 172px; border-width: 0;margin:0;padding:0; background: #FAFAFA; overflow:auto;">
- <ul data-bind="easyuiTree:tabletree"></ul>
- </div>
- </div>
- </div>
- <div id="wrapper">
- <div id="wizard" class="swMain" style="width:100%"></div>
- </div>
- </div>
- <script id="template-searchEdit" type="text/html">
- <ul>
- <li><a href="#step-1">
- <label class="stepNumber">1</label>
- <span class="stepDesc">设置条件部<br />
- <small>定义查询条件</small>
- </span>
- </a></li>
- <li><a href="#step-2">
- <label class="stepNumber">2</label>
- <span class="stepDesc">设置数据列<br />
- <small>定义查询显示的数据字段</small>
- </span>
- </a></li>
- <li><a href="#step-3">
- <label class="stepNumber">3</label>
- <span class="stepDesc">其它设置<br />
- <small>修改其它代码生成设置</small>
- </span>
- </a></li>
- </ul>
- <div id="step-1" class="step">
- <h2 class="StepTitle">***步 请勾选要查询的字段</h2>
- <div>
- <div style="width:200px;float:left;overflow:auto;" data-bind="autoheight:172">
- <ul data-bind="easyuiTree:searchEdit.columntree"></ul>
- </div>
- <div style="float:left;overflow:auto" data-bind="autoheight:172,autowidth:405">
- <table class="grid">
- <thead>
- <tr>
- <th style="width:50px">字段</th>
- <th style="width:120px">显示名称</th>
- <th style="width:120px">控件类型</th>
- @*<th >参数</th>*@
- <th style="width:80px">查询逻辑</th>
- </tr>
- </thead>
- <tbody data-bind="foreach:form.conditions">
- <tr data-bind="attr:{id:$index}">
- <td data-bind="text:field" style="text-align:left"></td>
- <td><input type="text" class="z-txt" data-bind="value:title"/></td>
- <td><select class="z-txt" data-bind="options:$root.data.input,value:type"></select></td>
- @*<td><input type="text" class="z-txt" data-bind="value:options"/></td>*@
- <td><select class="z-txt" data-bind="options:$root.data.compare,value:cp"></select></td>
- </tr>
- </tbody>
- </table>
- </div>
- </div>
- </div>
- <div id="step-2" class="step">
- <h2 class="StepTitle">第二步 请勾选要显示的数据字段</h2>
- <div style="width:200px;float:left;overflow:auto;" data-bind="autoheight:172">
- <ul data-bind="easyuiTree:searchEdit.columntree2"></ul>
- </div>
- <div style="float:left;overflow:auto" data-bind="autoheight:172,autowidth:405">
- <table class="grid">
- <thead>
- <tr>
- <th style="width:50px">字段</th>
- <th style="width:100px">题头</th>
- <th style="width:30px">隐藏</th>
- <th style="width:30px">排序</th>
- <th style="width:50px">对齐</th>
- <th style="width:40px">宽度</th>
- <th style="width:50px">格式化</th>
- <th style="width:50px">编辑器</th>
- </tr>
- </thead>
- <tbody data-bind="foreach:form.columns">
- <tr data-bind="attr:{id:$index}">
- <td data-bind="text:field" style="text-align:left"></td>
- <td><input type="text" class="z-txt" data-bind="value:title" /></td>
- <td><input type="checkbox" data-bind="checked:hidden"/></td>
- <td><input type="checkbox" data-bind="checked:sortable"/></td>
- <td><select class="z-txt" data-bind="options:$root.data.align,value:align" ></select></td>
- <td><input type="text" class="z-txt" data-bind="value:width" /></td>
- <td><select class="z-txt" data-bind="options:$root.data.formatter,optionsText:'text',optionsValue:'value',value:formatter" ></select></td>
- <td><select class="z-txt" data-bind="options:$root.data.editor,optionsText:'text',optionsValue:'value',value:editor" ></select></td>
- </tr>
- </tbody>
- </table>
- </div>
- </div>
- <div id="step-3" class="step">
- <h2 class="StepTitle">第三步 其它设置</h2>
- <div class="container_12">
- <div class="grid_1 lbl">业务区域</div>
- <div class="grid_2 val"><input type="text" class="z-txt" data-bind="value:form.area"/></div>
- <div class="clear"></div>
- <div class="grid_1 lbl">控制器名称</div>
- <div class="grid_2 val"><input type="text" class="z-txt" data-bind="value:form.controller"/></div>
- <div class="clear"></div>
- <div class="grid_1 lbl">生成路径</div>
- <div class="grid_2 val"><input type="text" class="z-txt" data-bind="value:form.path"/></div>
- </div>
- </div>
- </script>
- <script id="template-search" type="text/html">
- <ul>
- <li><a href="#step-1">
- <label class="stepNumber">1</label>
- <span class="stepDesc">设置条件部<br />
- <small>定义查询条件</small>
- </span>
- </a></li>
- <li><a href="#step-2">
- <label class="stepNumber">2</label>
- <span class="stepDesc">设置数据列<br />
- <small>定义查询显示的数据字段</small>
- </span>
- </a></li>
- <li><a href="#step-3">
- <label class="stepNumber">3</label>
- <span class="stepDesc">其它设置<br />
- <small>修改其它代码生成设置</small>
- </span>
- </a></li>
- </ul>
- <div id="step-1" class="step">
- <h2 class="StepTitle">***步 请勾选要查询的字段</h2>
- <div>
- <div style="width:200px;float:left;overflow:auto;" data-bind="autoheight:172">
- <ul data-bind="easyuiTree:searchEdit.columntree"></ul>
- </div>
- <div style="float:left;overflow:auto" data-bind="autoheight:172,autowidth:405">
- <table class="grid">
- <thead>
- <tr>
- <th style="width:50px">字段</th>
- <th style="width:120px">显示名称</th>
- <th style="width:120px">控件类型</th>
- @*<th >参数</th>*@
- <th style="width:80px">查询逻辑</th>
- </tr>
- </thead>
- <tbody data-bind="foreach:form.conditions">
- <tr data-bind="attr:{id:$index}">
- <td data-bind="text:field" style="text-align:left"></td>
- <td><input type="text" class="z-txt" data-bind="value:title"/></td>
- <td><select class="z-txt" data-bind="options:$root.data.input,value:type"></select></td>
- @*<td><input type="text" class="z-txt" data-bind="value:options"/></td>*@
- <td><select class="z-txt" data-bind="options:$root.data.compare,value:cp"></select></td>
- </tr>
- </tbody>
- </table>
- </div>
- </div>
- </div>
- <div id="step-2" class="step">
- <h2 class="StepTitle">第二步 请勾选要显示的数据字段</h2>
- <div style="width:200px;float:left;overflow:auto;" data-bind="autoheight:172">
- <ul data-bind="easyuiTree:searchEdit.columntree2"></ul>
- </div>
- <div style="float:left;overflow:auto" data-bind="autoheight:172,autowidth:405">
- <table class="grid">
- <thead>
- <tr>
- <th style="width:50px">字段</th>
- <th style="width:100px">题头</th>
- <th style="width:30px">隐藏</th>
- <th style="width:30px">排序</th>
- <th style="width:50px">对齐</th>
- <th style="width:40px">宽度</th>
- <th style="width:50px">格式化</th>
- </tr>
- </thead>
- <tbody data-bind="foreach:form.columns">
- <tr data-bind="attr:{id:$index}">
- <td data-bind="text:field" style="text-align:left"></td>
- <td><input type="text" class="z-txt" data-bind="value:title" /></td>
- <td><input type="checkbox" data-bind="checked:hidden"/></td>
- <td><input type="checkbox" data-bind="checked:sortable"/></td>
- <td><select class="z-txt" data-bind="options:$root.data.align,value:align" ></select></td>
- <td><input type="text" class="z-txt" data-bind="value:width" /></td>
- <td><select class="z-txt" data-bind="options:$root.data.formatter,optionsText:'text',optionsValue:'value',value:formatter" ></select></td>
- </tr>
- </tbody>
- </table>
- </div>
- </div>
- <div id="step-3" class="step">
- <h2 class="StepTitle">第三步 其它设置</h2>
- <div class="container_12">
- <div class="grid_1 lbl">业务区域</div>
- <div class="grid_2 val"><input type="text" class="z-txt" data-bind="value:form.area"/></div>
- <div class="clear"></div>
- <div class="grid_1 lbl">控制器名称</div>
- <div class="grid_2 val"><input type="text" class="z-txt" data-bind="value:form.controller"/></div>
- <div class="clear"></div>
- <div class="grid_1 lbl">生成路径</div>
- <div class="grid_2 val"><input type="text" class="z-txt" data-bind="value:form.path"/></div>
- </div>
- </div>
- </script>
- <script id="template-edit" type="text/html">
- <ul>
- <li><a href="#step-1">
- <label class="stepNumber">1</label>
- <span class="stepDesc">设置主表编辑区<br />
- <small>定义主表编辑字段</small>
- </span>
- </a></li>
- <li><a href="#step-2">
- <label class="stepNumber">2</label>
- <span class="stepDesc">设置明细数据页签<br />
- <small>定义明细表及页签</small>
- </span>
- </a></li>
- <li><a href="#step-3">
- <label class="stepNumber">3</label>
- <span class="stepDesc">其它设置<br />
- <small>修改其它代码生成设置</small>
- </span>
- </a></li>
- </ul>
- <div id="step-1" class="step">
- <h2 class="StepTitle">***步 请勾选要编辑的字段</h2>
- <div>
- <div style="width:200px;float:left;overflow:auto;" data-bind="autoheight:172">
- <ul data-bind="easyuiTree:searchEdit.columntree"></ul>
- </div>
- <div style="float:left;overflow:auto" data-bind="autoheight:172,autowidth:405">
- <table class="grid">
- <thead>
- <tr>
- <th style="width:20%">字段</th>
- <th style="width:40%">标签名称</th>
- <th style="width:30%">控件类型</th>
- <th style="width:10%">只读</th>
- </tr>
- </thead>
- <tbody data-bind="foreach:form.conditions">
- <tr data-bind="attr:{id:$index}">
- <td data-bind="text:field" style="text-align:left"></td>
- <td><input type="text" class="z-txt" data-bind="value:title"/></td>
- <td><select class="z-txt" data-bind="options:$root.data.input,value:type" style="width:60%"></select><a href="#">高级</a></td>
- <td><input type="checkbox" data-bind="checked:readonly"/></td>
- </tr>
- </tbody>
- </table>
- </div>
- </div>
- </div>
- <div id="step-2" class="step">
- <h2 class="StepTitle">第二步 请设置页面中的tab页签</h2>
- <div style="float:left;overflow:auto;width:150px;" data-bind="autoheight:172">
- <a href="#" class="buttonNext" style="float:left;margin:5px 3px 5px 0" data-bind="click:edit.addTab">添加Tab页签</a>
- <table class="grid">
- <thead>
- <tr>
- <th style="width:30%">#</th>
- <th style="width:70%">名称</th>
- </tr>
- </thead>
- <tbody data-bind="foreach:form.tabs">
- <tr data-bind="attr:{id:$index}">
- <td><a href="#" data-bind="click:$parent.edit.removeTab">删除</a></td>
- <td><input type="text" class="z-txt" data-bind="value:title,click:$parent.edit.clickTab" style="width:90%"/></td>
- </tr>
- </tbody>
- </table>
- </div>
- <div id="edit-tab-setting" style="float:left;overflow:auto;" data-bind="autoheight:172,autowidth:355,visible:edit.selectedTitle()!=null">
- </div>
- </div>
- <div id="step-3" class="step">
- <h2 class="StepTitle">第三步 其它设置</h2>
- <div class="container_12">
- <div class="grid_1 lbl">业务区域</div>
- <div class="grid_2 val"><input type="text" class="z-txt" data-bind="value:form.area"/></div>
- <div class="clear"></div>
- <div class="grid_1 lbl">控制器名称</div>
- <div class="grid_2 val"><input type="text" class="z-txt" data-bind="value:form.controller"/></div>
- <div class="clear"></div>
- <div class="grid_1 lbl">生成路径</div>
- <div class="grid_2 val"><input type="text" class="z-txt" data-bind="value:form.path"/></div>
- </div>
- </div>
- </script>
- <script type="text/html" id="template-edit-tab-setting">
- <div style="padding:8px;clear:both">
- <span>页签类型 </span>
- <select class="z-txt" style="padding:0;height:22px;" data-bind="value:edit.selectedTab.type">
- <option value="empty">empty</option>
- <option value="grid">grid</option>
- <option value="form">form</option>
- </select>
- <span data-bind="visible:edit.selectedTab.type()!='empty'"> 数据表 </span>
- <select class="z-txt" style="padding:0;height:22px;" data-bind="options:data.table,optionsText:'text',optionsValue:'id',value:edit.selectedTab.subtable,visible:edit.selectedTab.type()!='empty'"></select>
- <span data-bind="visible:edit.selectedTab.type()!='empty'">与主表的关联</span>
- <select class="z-txt" style="padding:0;height:22px;" data-bind="options:data.tablekey,value:edit.selectedTab.relationship,visible:edit.selectedTab.type()!='empty'"></select>
- </div>
- <div style="width:180px;float:left;overflow:auto;margin-right:-18px;" data-bind="autoheight:212,visible:edit.selectedTab.type()!='empty'">
- <ul data-bind="easyuiTree:edit.columntree2"></ul>
- </div>
- <div style="float:right;overflow:auto;" data-bind="autoheight:210,autowidth:535,visible:edit.selectedTab.type()!='empty'">
- <table class="grid">
- <thead>
- <tr>
- <th style="width:50px">字段</th>
- <th style="width:100px">题头</th>
- <th style="width:30px" data-bind="visible:edit.selectedTab.type()=='grid'">隐藏</th>
- <th style="width:30px" data-bind="visible:edit.selectedTab.type()=='grid'">排序</th>
- <th style="width:50px" data-bind="visible:edit.selectedTab.type()=='grid'">对齐</th>
- <th style="width:40px" data-bind="visible:edit.selectedTab.type()=='grid'">宽度</th>
- <th style="width:50px" data-bind="visible:edit.selectedTab.type()=='grid'">格式化</th>
- <th style="width:50px" data-bind="visible:edit.selectedTab.type()=='grid'">编辑器</th>
- <th style="width:50px" data-bind="visible:edit.selectedTab.type()=='form'">控件类型</th>
- <th style="width:10px" data-bind="visible:edit.selectedTab.type()=='form'">只读</th>
- </tr>
- </thead>
- <tbody data-bind="foreach:edit.selectedTab.columns">
- <tr data-bind="attr:{id:$index}">
- <td data-bind="text:field" style="text-align:left"></td>
- <td><input type="text" class="z-txt" data-bind="value:title" /></td>
- <td data-bind="visible:$parent.edit.selectedTab.type()=='grid'"><input type="checkbox" data-bind="checked:hidden"/></td>
- <td data-bind="visible:$parent.edit.selectedTab.type()=='grid'"><input type="checkbox" data-bind="checked:sortable"/></td>
- <td data-bind="visible:$parent.edit.selectedTab.type()=='grid'"><select class="z-txt" data-bind="options:$root.data.align,value:align" ></select></td>
- <td data-bind="visible:$parent.edit.selectedTab.type()=='grid'"><input type="text" class="z-txt" data-bind="value:width" /></td>
- <td data-bind="visible:$parent.edit.selectedTab.type()=='grid'"><select class="z-txt" data-bind="options:$root.data.formatter,optionsText:'text',optionsValue:'value',value:formatter" ></select></td>
- <td data-bind="visible:$parent.edit.selectedTab.type()=='grid'"><select class="z-txt" data-bind="options:$root.data.editor,optionsText:'text',optionsValue:'value',value:editor" ></select></td>
- <td data-bind="visible:$parent.edit.selectedTab.type()=='form'"><select class="z-txt" data-bind="options:$root.data.input,value:type"></select></td>
- <td data-bind="visible:$parent.edit.selectedTab.type()=='form'"><input type="checkbox" data-bind="checked:readonly"/></td>
- </tr>
- </tbody>
- </table>
- </div>
- </script>
Generator.js
- /**
- * 模块名:mms viewModel
- * 程序名: Generator.js
- * Copyright(c) 2013 liuhuisheng [ liuhuisheng.xm@gmail.com ]
- **/
- var viewModel = function () {
- var self = this;
- this.form = {
- type: '',
- database:ko.observable(),
- table: ko.observable(),
- controller: ko.observable(),
- area:ko.observable(),
- conditions: ko.observableArray(),
- columns: ko.observableArray(),
- tabs: ko.observableArray(),
- path: ko.observable("~/Generator/")
- };
- this.resetForm = function () {
- self.form.conditions([]);
- self.form.columns([]);
- self.form.tabs([]);
- };
- this.data = {
- codetype: [{ text: 'search', value: 'search' }, { text: 'edit', value: 'edit' }, { text: 'searchEdit', value: 'searchEdit' }],
- database: ko.observableArray(),
- table: ko.observableArray(),
- column:ko.observableArray(),
- tablekey: ko.observableArray(),
- input: ['text', 'autocomplete', 'combobox', 'lookup','datebox','daterange'],
- compare: ['equal', 'like', 'startwith', 'endwith', 'greater', 'less', 'daterange'],
- align:['left','center','right'],
- formatter: [{text:'',value:''},{ text: '日期', value: 'com.formatDate' }, { text: '时间', value: 'com.formatTime' }, { text: '金额', value: 'com.formatMoney' }, { text: '是否', value: 'com.formatCheckbox' }],
- editor: [{text:'',value:''},{ text: '文本', value: 'text'}, { text: '整数', value: "{type: 'numberbox',options:{min: 0}}" }, { text: '两位小数', value: "{type: 'numberbox',options:{min: 0, precision: 2}}" }, { text: '下拉框', value: "{type:'combobox',options:{}}" }, { text: '弹出框', value: "{type:'lookup',options:{}}" }, { text: '日期', value: 'datebox' }]
- };
- this.initDatabase = function () {
- com.ajax({
- type: 'GET',
- async:false,
- url: '/api/sys/generator/GetConnectionStrings',
- success: function (d) {
- self.data.database(d);
- }
- });
- };
- this.initDatabase();
- this.getTableUrl = function () {
- return '/api/sys/generator/GetTables?database=' + self.form.database();
- };
- this.getColumnUrl = function (table) {
- return '/api/sys/generator/GetColumns?database=' + self.form.database() + "&table=" + table;
- }
- this.codetype = {
- showblank: true,
- width: 110,
- data: self.data.codetype,
- onSelect: function (node) {
- self.form.type = node.value;
- self.initWizard();
- }
- };
- this.database = {
- showblank: true,
- width: 110,
- data: self.data.database,
- onSelect: function (node) {
- self.form.database(node.value)
- self.form.area((node.value.split('.')[1] || node.value).replace(/(^|\s+)\w/g, function (s) { return s.toUpperCase(); }));
- }
- };
- this.tabletree = {
- method: 'GET',
- url: ko.computed(self.getTableUrl),
- loadFilter: function (d) {
- var data = utils.filterProperties(d.rows || d, ['TableName as id', 'TableName as text']);
- self.data.table(data);
- return data;
- },
- onSelect: function (node) {
- self.form.table(node.id);
- self.edit.init();
- self.resetWizard();
- self.form.controller((node.id.split('_')[1] || node.id).replace(/(^|\s+)\w/g, function (s) { return s.toUpperCase(); }));
- }
- };
- this.generator = function () {
- com.ajax({
- type:'POST',
- url: '/api/sys/generator',
- data: ko.toJSON(self.form),
- success: function (d) {
- com.message('success', "代码已生成!");
- }
- });
- };
- this.searchEdit = {};
- this.searchEdit.columntree = {
- method: 'GET',
- url: ko.computed(function () {
- return self.getColumnUrl(self.form.table());
- }),
- checkbox: true,
- loadFilter: function (d) {
- return utils.filterProperties(d.rows || d, ['ColumnName as id', 'ColumnName as text']);
- },
- onSelect: function (node) {
- var handle = node.checked ? 'uncheck' : 'check';
- $(this).tree(handle, node.target);
- },
- onCheck: function (node, checked) {
- if (checked)
- self.form.conditions.push({ field: node.id, title: node.id, type: 'text', options: '', cp: 'equal',readonly:false });
- else
- self.form.conditions.remove(function (item) { return item.field == node.id });
- },
- onLoadSuccess: self.resetForm
- };
- this.searchEdit.columntree2 = {
- method: 'GET',
- url: ko.computed(function () {
- return self.getColumnUrl(self.form.table());
- }),
- checkbox: true,
- loadFilter: function (d) {
- return utils.filterProperties(d.rows || d, ['ColumnName as id', 'ColumnName as text']);
- },
- onSelect: function (node) {
- var handle = node.checked ? 'uncheck' : 'check';
- $(this).tree(handle, node.target);
- },
- onCheck: function (node, checked) {
- var arr = self.form.columns;
- if (checked) {
- var item = $.grep(arr(), function (row) {return row.field == node.id;})[0];
- item || arr.push({ field: node.id, title: node.id, hidden: false, sortable: true, align: 'left', width: 80, formatter: '', editor: 'text' });
- } else
- arr.remove(function (item) { return item.field == node.id });
- }
- };
- this.edit = {};
- this.edit.selectedTab = {
- title: ko.observable(),
- type: ko.observable(),
- subtable: ko.observable(),
- relationship: ko.observable(),
- columns: ko.observableArray(),
- primaryKeys:ko.observableArray()
- };
- this.edit.columntree2 = {
- method: 'GET',
- url:ko.observable(),
- checkbox: true,
- loadFilter: function (d) {
- self.data.column(d);
- var list = utils.filterProperties(d.rows || d, ['ColumnName as id', 'ColumnName as text']);
- self.edit.setDefaultForm();
- self.edit.resetTableKey();
- var checkedList = [];
- for (var i in self.edit.selectedTab.columns())
- checkedList.push(self.edit.selectedTab.columns()[i].field);
- for (var i in list)
- if ($.inArray(list[i].id, checkedList) > -1) list[i].checked = true;
- return list
- },
- onSelect: function (node) {
- var handle = node.checked ? 'uncheck' : 'check';
- $(this).tree(handle, node.target);
- },
- onCheck: function (node, checked) {
- var arr = self.edit.selectedTab.columns;
- if (checked) {
- var item = $.grep(arr(), function (row) { return row.field == node.id; })[0];
- item || arr.push({ field: node.id, title: node.id, hidden: false, sortable: true, align: 'left', width: 80, formatter: '', editor: 'text', type: '', readonly: true });
- } else
- arr.remove(function (item) { return item.field == node.id });
- }
- }
- this.edit.init = function () {
- self.edit.selectedTitle(null);
- self.edit.selectedTab = null;
- $('#edit-tab-setting').empty();
- };
- this.edit.addTab = function () {
- var title = 'tab' + (self.form.tabs().length + 1);
- var newTab = {
- title: ko.observable(title),
- type: ko.observable('empty'),
- subtable: ko.observable(self.form.table()),
- relationship: ko.observable(),
- columns: ko.observableArray(),
- primaryKeys:ko.observableArray()
- };
- newTab.type.subscribe(function (value) {
- if (value == 'grid') {
- var item = $.grep(self.data.table(), function (row) { return row.id == self.form.table() + "Detail" })[0];
- if (item)
- newTab.subtable(item.id);
- }
- else if (value == 'form') {
- newTab.subtable(self.form.table());
- }
- });
- newTab.columns.subscribe(self.tableDnDUpdate);
- newTab.subtable.subscribe(function (value) {
- self.edit.selectedTab.columns([]);
- self.edit.columntree2.url(self.getColumnUrl(value));
- });
- self.form.tabs.push(newTab);
- };
- this.edit.removeTab = function (row,event) {
- self.form.tabs.remove(row);
- if (row.title() == self.edit.selectedTitle())
- self.edit.selectedTitle(null);
- };
- this.edit.selectedTitle = ko.observable();
- this.edit.clickTab = function (row, event) {
- if (row.title() == self.edit.selectedTitle()) return;
- self.edit.selectedTitle(row.title());
- self.edit.selectedTab = row;
- self.edit.columntree2.url = ko.observable(self.getColumnUrl(self.edit.selectedTab.subtable()));
- var currentTr = $(event.srcElement).parent("td").parent("tr");
- currentTr.parent().find("tr.tree-node-selected").removeClass("tree-node-selected");
- currentTr.addClass("tree-node-selected");
- var tabTemplate = $('#template-edit-tab-setting').html();
- var wrapper = $('#edit-tab-setting').empty().html(tabTemplate);
- ko.cleanNode(wrapper[0]);
- ko.applyBindings(self, wrapper[0]);
- wrapper.find("table").tableDnD({ onDrop: self.tableDnDSort });
- };
- this.edit.resetTableKey = function () {
- var relationship = self.edit.selectedTab.relationship();
- self.data.tablekey([]);
- var cols = self.data.column();
- for (var i in cols)
- if (cols[i].IsIdentity || cols[i].IsPrimaryKey)
- self.data.tablekey.push(cols[i].ColumnName);
- self.edit.selectedTab.relationship(relationship);
- self.edit.selectedTab.primaryKeys(self.data.tablekey());
- };
- this.edit.setDefaultForm = function () {
- var arr = [
- { field: 'ApproveState', title: '审批状态', type: 'text', readonly: true },
- { field: 'ApproveRemark', title: '审批意见', type: 'text', readonly: true },
- { field: 'ApprovePerson', title: '审批人', type: 'text', readonly: true },
- { field: 'ApproveDate', title: '审批日期', type: 'datebox', readonly: true },
- { field: 'CreatePerson', title: '编制人', type: 'text', readonly: true },
- { field: 'CreateDate', title: '编制日期', type: 'datebox', readonly: true },
- { field: 'UpdatePerson', title: '修改人', type: 'text', readonly: true },
- { field: 'UpdateDate', title: '修改日期', type: 'datebox', readonly: true }
- ];
- var cols = self.data.column();
- var defaults = { field: '', title: '', hidden: false, sortable: true, align: 'left', width: 80, formatter: '', editor: 'text', type: '', readonly: true };
- for (var i in arr) {
- if (!$.grep(cols, function (item) { return item.ColumnName == arr[i].field; }).length)
- return;
- arr[i] = $.extend({}, defaults, arr[i]);
- }
- self.edit.selectedTab.columns(arr);
- var tree = self.edit.columntree2.$element();
- for (var i in arr) {
- var node = tree.tree('find', arr[i].field);
- if (node) tree.tree('check', node.target);
- }
- };
- this.initWizard = function () {
- var stepTemplate = $('#template-' + self.form.type);
- if (!stepTemplate.length) return;
- var wizard = $('#wizard').removeData('smartWizard').empty();
- ko.cleanNode(wizard[0]);
- wizard.html(stepTemplate.html());
- wizard.smartWizard({
- labelNext: '下一步',
- labelPrevious: '上一步',
- labelFinish: '生成',
- onFinish: self.generator
- });
- var resizeStep = function () {
- $(".step").height($(window).height() - 145)
- .width($(window).width() - 205);
- $(".actionBar").width($(window).width() - 195);
- var index = wizard.smartWizard('currentStep');
- wizard.smartWizard('goToStep', index);
- };
- $(window).resize(resizeStep);
- resizeStep();
- ko.applyBindings(self, wizard[0]);
- wizard.find("table").tableDnD({ onDrop: self.tableDnDSort });
- for (var i in self.form) {
- if ($.isFunction(self.form[i]))
- if (self.form[i]() instanceof Array)
- if (self.form[i].subscribe)
- self.form[i].subscribe(self.tableDnDUpdate);
- }
- };
- this.resetWizard = function () {
- var wizard = $("#wizard").smartWizard('goToStep', 1);
- for (var i = 1; i <= wizard.find(">ul>li").length; i++)
- wizard.smartWizard("disableStep", i);
- };
- this.tableDnDUpdate = function () {
- setTimeout('$("table").tableDnDUpdate()', 300);
- };
- this.tableDnDSort = function (table, row) {
- var name = $(table).find("tbody").attr("data-bind").replace('foreach:form.','');
- var array = self.form[name], i = 0;
- if (name == 'foreach:edit.selectedTab.columns')
- array = self.edit.selectedTab.columns;
- $("tr[id]", table).each(function () { array()[this.id].id = i++; });
- array.sort(function (left, right) { return left.id == right.id ? 0 : (left.id < right.id ? -1 : 1) });
- //for fix ko bug refresh ui
- var tempArr = array();
- array([]);
- array(tempArr);
- };
- };
razor模板
model.cshtml
- @using Zephyr.Core.Generator
- using System;
- using System.Collections.Generic;
- using System.Text;
- using Zephyr.Core;
- namespace Zephyr.Web.@(@Model.Area).Models
- {
- [Module("@Model.Database")]
- public class @(Model.TableName)Service : ServiceBase<@Model.TableName>
- {
- }
- public class @Model.TableName : ModelBase
- {
- @foreach(TableSchema item in Model.Columns)
- {
- if (item.IsIdentity)
- {
- @:[Identity]
- }
- if (item.IsPrimaryKey)
- {
- @:[PrimaryKey]
- }
- @:public @item.TypeName @item.ColumnName { get; set; }
- }
- }
- }
其它各页面的模板我就不一一贴出来了,大家可以查看我以前的那些关于共通viewModel的博客,查询及编辑页面我都有详细介绍过,大家也可以自己提炼出自己的一套模板,然后也可以用这同一个思路去做一个代码生成器。
七、后述
有了这个代码生成的功能,5分钟做一个基本的页面应该是完全没有问题的。
我这里分享下自己的代码生成的思路,权当抛砖引玉,大家有什么更好的方法欢迎留言。