快速开发之代码生成器(asp.net mvc4+easyui+knockoutjs)

2023-10-19 21:40

本文主要是介绍快速开发之代码生成器(asp.net mvc4+easyui+knockoutjs),希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

一、前言

作为一个码农这么多年,一直在想怎么提高我们的编码效率,关于如何提高编码效率,我自己的几点体会

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、查询页面
这个模板可以解决大部分的查询报表业务功能
image

2、编辑页面
这个编辑模板可以解决基本上所有的录入功能,因为包括了主表,及多个从表(1:N或1:1)录入,而且可以一次性在同一事务中保存。并且定义了很多触发前后事件用于写一些业务处理,并且做到差异更新。
image

3、查询编辑页面,可以查询也可以直接在grid中编辑,这个页面用于做一些简单单据或一些基础数据页面。image

四、代码生成原理

把以上页面代码做成razor模板,razor模板 + 设定选项 ==razor引擎==> 页面代码

怎么利用razor引擎,其实有以下几种方法:
1、直接利用mvc的view输出功能,以下为关键代码

view source print ?
1. var stringWriter = new StringWriter();
2. var viewContext = new ViewContext(controllerContext, view, viewData, TempData, stringWriter);
3. view.Render(viewContext, stringWriter);
4. var result = stringWriter.ToString();

用这种方法的优点在于不需要引入第三方类库,直接调用mvc视图的Render方法生成,而且效率很高,缺点是controllerContext及view对象的构建获取非常复杂。这种方法适用于有洁辟的码农们,我属于这一种。

2、利用第三方类库RazorEngine输出,以下为关键代码

view source print ?
1. var template = 'Hello @Model.Name! Welcome to Razor!';
view source print ?
1. var viewData = new { Name = 'World' });
2. var result = Razor.Parse(template, viewData);

这代码很清爽,一目了然,只不过要引入RazorEngine类库,而且效率不如前者。

五、代码生成用户界面

我们模板准备好了,引擎准备好了,那么还需要一个数据输入viewData,我们做用户界面的目的也就是为了更好的定义这个viewData。
这个用户界面我们还是要把三种页面的定义分开:

1、查询页面生成

第一步,选择代码类别search(查询页面),选择数据库,选择业务主表,再勾选字段即可实现查询条件部的设置,并且实现了拖拉排序功能。大家可以对照查询模板看。
image

第二步,选择grid中要显示的列,并且设置属性,格式化等
image

第三步,设置一些全局设定,主要根据这些参数确定命名空间,生成文件名等信息
image

点击生成按钮,按设定生成代码,生成后弹出文件夹,已分别生成MVC三层代码
image

mms_receive.cs
image

Index.cshtml
image

ReceiveController.cs
image

把这个代码直接拷贝到项目中直接运行,测试条件过滤都没有问题,grid会自适应高度,grid远程排序,选择分页翻页都没有问题,所有的功能都可用,
只有lookup控件弹出是空值,因为只把控制设置为了lookup但没有为它设置更详细的选项。autocomplete也是同样
即代码生成器已经生成了一个大的结构及UI,一些小细节还是要手动修改下,代码生成的UI界面如果把每个控件的选项也做进去会相当的复杂,也没有必要再细化了。
image

2、编辑页面生成
第一步,选择主表编辑区的字段及控件类型,控件类型中的高级还未实现,这个编辑的UI也可以参照编辑的模板看
image

第二步,添加tab页签,选择页签类型(grid,form,empty) grid是指跟主表N:1关系,form是指跟主表1:1关系,empty是空页签,生成后自己可以添加内容
这里我随便添加三个tab页签tab1 tab2 tab3
tab1用来放人员变动grid(跟主表关系N:1)image

tab2就选择form(跟主表关系1:1,也可以是主表本身)image

tab3也随便添些东西
image

第三步,其它设置
image

点击生成按钮,生成后自动打开文件夹
image

把这些代码拷贝到项目中直接运行
image

tab2
image

tab3,修改主表数据,tab1,tab2,tab3点保存,能保存成功,
image

审核按钮也可用,审核后单据不可修改
image

这个编辑功能基本上可以囊括很多的录入页面了,可以算是比较通用了

3、查询编辑页面(查询编辑在同一个页面内)页面生成

第一步,选择查询条件并设置控件类型image

第二步,设置grid中的数据列,及编辑器
image

第三步,其它设置
image

点击生成按钮,生成后自动打开文件夹 
image

把代码直接拷贝到项目中运行,结果如下,经测试除了控件还需要进一步设置,所有按钮功能正常使用
image

六、代码生成页面的源码

UI展现主要是用了easyui 及jquery插件smartwizard
前端交互主要是采用了knockoutjs
table表格的行拖拉是采用jquery插件tableDnD
后台用webapi来处理请求,代码有点长:

Index.cshtml

view source print ?
001. @{
002. ViewBag.Title = '代码生成';
003. Layout = '~/Views/Shared/_Layout.cshtml';
004. }
005.  
006. @section head{
007. <link href='~/Content/js/jquery-plugins/smartwizard/smart_wizard.css' rel='stylesheet' />
008. <style type='text/css'>
009. div#navigation{float: left;width: 180px;}
010. div#wrapper{float: right;width: 100%;margin-left: -185px;}
011. div#wizard{margin-left: 185px;}
012. ul.anchor{margin:0 0 10px 0 !important;}
013. ul li{margin-left:-16px;}
014. .grid .z-txt{margin:0 -3px;width:90%;}
015. .grid input{width:90%;}
016. .grid input[type=checkbox]{cursor:default;}
017. .grid select{width:80%;padding:0 !important;height:22px;}
018. .grid select + a{margin:5px;}
019. .tDnD_whileDrag{background-color: #FBEC88 !important;}
020. </style>
021. }
022.  
023. @section scripts{
024. <script src='~/Content/js/jquery-plugins/smartwizard/jquery.smartWizard.js'></script>
025. <script src='~/Content/js/jquery-extend/jquery.tablednd.js'></script>
026. @Scripts.Render('~/Resource/Sys/Generator.js')
027. <script type='text/javascript'>
028. $(function () {
029. ko.applyBindings(new viewModel());
030. });
031. </script>
032. }
033.  
034. <div id='container'>
035. <div id='navigation'>
036. <div class='panel-header' style='width: 168px; border-width: 0; background: #FAFAFA;'>
037. 代码类别
038. <input type='text' class='z-txt' data-bind='easyuiCombobox:codetype' />
039. <div style='margin:1px;'></div>
040. 数据库名
041. <input type='text' class='z-txt' data-bind='easyuiCombobox:database' />
042.  
043. <div style='margin:5px;'></div>
044. <div  data-bind='autoheight:60'  style='width: 172px; border-width: 0;margin:0;padding:0; background: #FAFAFA; overflow:auto;'>
045. <ul data-bind='easyuiTree:tabletree'></ul>
046. </div>
047. </div>
048. </div>
049. <div id='wrapper'>
050. <div id='wizard' class='swMain' style='width:100%'></div>
051. </div>
052. </div>
053.  
054. <script id='template-searchEdit' type='text/html'>
055. <ul>
056. <li><a href='#step-1'>
057. <label class='stepNumber'>1</label>
058. <span class='stepDesc'>设置条件部<br />
059. <small>定义查询条件</small>
060. </span>
061. </a></li>
062. <li><a href='#step-2'>
063. <label class='stepNumber'>2</label>
064. <span class='stepDesc'>设置数据列<br />
065. <small>定义查询显示的数据字段</small>
066. </span>
067. </a></li>
068. <li><a href='#step-3'>
069. <label class='stepNumber'>3</label>
070. <span class='stepDesc'>其它设置<br />
071. <small>修改其它代码生成设置</small>
072. </span>
073. </a></li>
074. </ul>
075.  
076. <div id='step-1' class='step'>
077. <h4 class='StepTitle'>第一步 请勾选要查询的字段</h4>
078. <div> 
079. <div style='width:200px;float:left;overflow:auto;' data-bind='autoheight:172'>
080. <ul data-bind='easyuiTree:searchEdit.columntree'></ul>
081. </div> 
082. <div style='float:left;overflow:auto' data-bind='autoheight:172,autowidth:405'>
083. <table class='grid'>
084. <thead>
085. <tr>
086. <th style='width:50px'>字段</th>
087. <th style='width:120px'>显示名称</th>
088. <th style='width:120px'>控件类型</th>
089. @*<th >参数</th>*@
090. <th style='width:80px'>查询逻辑</th>
091. </tr>
092. </thead>
093. <tbody data-bind='foreach:form.conditions'>
094. <tr data-bind='attr:{id:$index}'>
095. <td data-bind='text:field' style='text-align:left'></td>
096. <td><input type='text' class='z-txt' data-bind='value:title'/></td>
097. <td><select class='z-txt'  data-bind='options:$root.data.input,value:type'></select></td>
098. @*<td><input type='text' class='z-txt' data-bind='value:options'/></td>*@
099. <td><select class='z-txt'  data-bind='options:$root.data.compare,value:cp'></select></td>
100. </tr>
101.  
102. </tbody>
103. </table>
104. </div> 
105. </div>
106. </div>
107. <div id='step-2' class='step'>
108. <h4 class='StepTitle'>第二步 请勾选要显示的数据字段</h4>
109. <div style='width:200px;float:left;overflow:auto;' data-bind='autoheight:172'>
110. <ul data-bind='easyuiTree:searchEdit.columntree2'></ul>
111. </div> 
112. <div style='float:left;overflow:auto' data-bind='autoheight:172,autowidth:405'>
113. <table class='grid'>
114. <thead>
115. <tr>
116. <th style='width:50px'>字段</th>
117. <th style='width:100px'>题头</th>
118. <th style='width:30px'>隐藏</th>
119. <th style='width:30px'>排序</th>
120. <th style='width:50px'>对齐</th>
121. <th style='width:40px'>宽度</th>
122. <th style='width:50px'>格式化</th>
123. <th style='width:50px'>编辑器</th>
124. </tr>
125. </thead>
126. <tbody data-bind='foreach:form.columns'>
127. <tr data-bind='attr:{id:$index}'>
128. <td data-bind='text:field' style='text-align:left'></td>
129. <td><input type='text' class='z-txt' data-bind='value:title' /></td>
130. <td><input type='checkbox' data-bind='checked:hidden'/></td>
131. <td><input type='checkbox' data-bind='checked:sortable'/></td>
132. <td><select class='z-txt'  data-bind='options:$root.data.align,value:align' ></select></td>
133. <td><input type='text' class='z-txt' data-bind='value:width' /></td>
134. <td><select class='z-txt'  data-bind='options:$root.data.formatter,optionsText:'text',optionsValue:'value',value:formatter' ></select></td>
135. <td><select class='z-txt'  data-bind='options:$root.data.editor,optionsText:'text',optionsValue:'value',value:editor' ></select></td>
136. </tr>
137. </tbody>
138. </table>
139. </div> 
140. </div>
141.  
142. <div id='step-3' class='step'>
143. <h4 class='StepTitle'>第三步 其它设置</h4>
144.  
145. <div class='container_12'>
146. <div class='grid_1 lbl'>业务区域</div>
147. <div class='grid_2 val'><input type='text' class='z-txt' data-bind='value:form.area'/></div>
148.  
149. <div class='clear'></div>
150.  
151. <div class='grid_1 lbl'>控制器名称</div>
152. <div class='grid_2 val'><input type='text' class='z-txt' data-bind='value:form.controller'/></div>
153.  
154. <div class='clear'></div>
155.  
156. <div class='grid_1 lbl'>生成路径</div>
157. <div class='grid_2 val'><input type='text' class='z-txt' data-bind='value:form.path'/></div>
158. </div>
159. </div>
160. </script>
161.  
162. <script id='template-search' type='text/html'>
163. <ul>
164. <li><a href='#step-1'>
165. <label class='stepNumber'>1</label>
166. <span class='stepDesc'>设置条件部<br />
167. <small>定义查询条件</small>
168. </span>
169. </a></li>
170. <li><a href='#step-2'>
171. <label class='stepNumber'>2</label>
172. <span class='stepDesc'>设置数据列<br />
173. <small>定义查询显示的数据字段</small>
174. </span>
175. </a></li>
176. <li><a href='#step-3'>
177. <label class='stepNumber'>3</label>
178. <span class='stepDesc'>其它设置<br />
179. <small>修改其它代码生成设置</small>
180. </span>
181. </a></li>
182. </ul>
183.  
184. <div id='step-1' class='step'>
185. <h4 class='StepTitle'>第一步 请勾选要查询的字段</h4>
186. <div> 
187. <div style='width:200px;float:left;overflow:auto;' data-bind='autoheight:172'>
188. <ul data-bind='easyuiTree:searchEdit.columntree'></ul>
189. </div> 
190. <div style='float:left;overflow:auto' data-bind='autoheight:172,autowidth:405'>
191. <table class='grid'>
192. <thead>
193. <tr>
194. <th style='width:50px'>字段</th>
195. <th style='width:120px'>显示名称</th>
196. <th style='width:120px'>控件类型</th>
197. @*<th >参数</th>*@
198. <th style='width:80px'>查询逻辑</th>
199. </tr>
200. </thead>
201. <tbody data-bind='foreach:form.conditions'>
202. <tr data-bind='attr:{id:$index}'>
203. <td data-bind='text:field' style='text-align:left'></td>
204. <td><input type='text' class='z-txt' data-bind='value:title'/></td>
205. <td><select class='z-txt'  data-bind='options:$root.data.input,value:type'></select></td>
206. @*<td><input type='text' class='z-txt' data-bind='value:options'/></td>*@
207. <td><select class='z-txt'  data-bind='options:$root.data.compare,value:cp'></select></td>
208. </tr>
209.  
210. </tbody>
211. </table>
212. </div> 
213. </div>
214. </div>
215. <div id='step-2' class='step'>
216. <h4 class='StepTitle'>第二步 请勾选要显示的数据字段</h4>
217. <div style='width:200px;float:left;overflow:auto;' data-bind='autoheight:172'>
218. <ul data-bind='easyuiTree:searchEdit.columntree2'></ul>
219. </div> 
220. <div style='float:left;overflow:auto' data-bind='autoheight:172,autowidth:405'>
221. <table class='grid'>
222. <thead>
223. <tr>
224. <th style='width:50px'>字段</th>
225. <th style='width:100px'>题头</th>
226. <th style='width:30px'>隐藏</th>
227. <th style='width:30px'>排序</th>
228. <th style='width:50px'>对齐</th>
229. <th style='width:40px'>宽度</th>
230. <th style='width:50px'>格式化</th>
231. </tr>
232. </thead>
233. <tbody data-bind='foreach:form.columns'>
234. <tr data-bind='attr:{id:$index}'>
235. <td data-bind='text:field' style='text-align:left'></td>
236. <td><input type='text' class='z-txt' data-bind='value:title' /></td>
237. <td><input type='checkbox' data-bind='checked:hidden'/></td>
238. <td><input type='checkbox' data-bind='checked:sortable'/></td>
239. <td><select class='z-txt'  data-bind='options:$root.data.align,value:align' ></select></td>
240. <td><input type='text' class='z-txt' data-bind='value:width' /></td>
241. <td><select class='z-txt'  data-bind='options:$root.data.formatter,optionsText:'text',optionsValue:'value',value:formatter' ></select></td>
242. </tr>
243. </tbody>
244. </table>
245. </div> 
246. </div>
247.  
248. <div id='step-3' class='step'>
249. <h4 class='StepTitle'>第三步 其它设置</h4>
250.  
251. <div class='container_12'>
252. <div class='grid_1 lbl'>业务区域</div>
253. <div class='grid_2 val'><input type='text' class='z-txt' data-bind='value:form.area'/></div>
254.  
255. <div class='clear'></div>
256.  
257. <div class='grid_1 lbl'>控制器名称</div>
258. <div class='grid_2 val'><input type='text' class='z-txt' data-bind='value:form.controller'/></div>
259.  
260. <div class='clear'></div>
261.  
262. <div class='grid_1 lbl'>生成路径</div>
263. <div class='grid_2 val'><input type='text' class='z-txt' data-bind='value:form.path'/></div>
264. </div>
265. </div>
266. </script>
267.  
268. <script id='template-edit' type='text/html'>
269. <ul>
270. <li><a href='#step-1'>
271. <label class='stepNumber'>1</label>
272. <span class='stepDesc'>设置主表编辑区<br />
273. <small>定义主表编辑字段</small>
274. </span>
275. </a></li>
276. <li><a href='#step-2'>
277. <label class='stepNumber'>2</label>
278. <span class='stepDesc'>设置明细数据页签<br />
279. <small>定义明细表及页签</small>
280. </span>
281. </a></li>
282. <li><a href='#step-3'>
283. <label class='stepNumber'>3</label>
284. <span class='stepDesc'>其它设置<br />
285. <small>修改其它代码生成设置</small>
286. </span>
287. </a></li>
288. </ul>
289.  
290. <div id='step-1' class='step'>
291. <h4 class='StepTitle'>第一步 请勾选要编辑的字段</h4>
292. <div> 
293. <div style='width:200px;float:left;overflow:auto;' data-bind='autoheight:172'>
294. <ul data-bind='easyuiTree:searchEdit.columntree'></ul>
295. </div> 
296. <div style='float:left;overflow:auto' data-bind='autoheight:172,autowidth:405'>
297. <table class='grid'>
298. <thead>
299. <tr>
300. <th style='width:20%'>字段</th>
301. <th style='width:40%'>标签名称</th>
302. <th style='width:30%'>控件类型</th>
303. <th style='width:10%'>只读</th>
304. </tr>
305. </thead>
306. <tbody data-bind='foreach:form.conditions'>
307. <tr data-bind='attr:{id:$index}'>
308. <td data-bind='text:field' style='text-align:left'></td>
309. <td><input type='text' class='z-txt' data-bind='value:title'/></td>
310. <td><select class='z-txt'  data-bind='options:$root.data.input,value:type' style='width:60%'></select><a href='#'>高级</a></td>
311. <td><input type='checkbox' data-bind='checked:readonly'/></td>
312. </tr>
313. </tbody>
314. </table>
315. </div> 
316. </div>
317. </div>
318. <div id='step-2' class='step'>
319. <h4 class='StepTitle'>第二步 请设置页面中的tab页签</h4>
320.  
321. <div style='float:left;overflow:auto;width:150px;' data-bind='autoheight:172'>
322. <a href='#' class='buttonNext' style='float:left;margin:5px 3px 5px 0' data-bind='click:edit.addTab'>添加Tab页签</a>
323. <table class='grid'>
324. <thead>
325. <tr>
326. <th style='width:30%'>#</th>
327. <th style='width:70%'>名称</th>
328. </tr>
329. </thead>
330. <tbody data-bind='foreach:form.tabs'>
331. <tr data-bind='attr:{id:$index}'>
332. <td><a href='#' data-bind='click:$parent.edit.removeTab'>删除</a></td>
333. <td><input type='text' class='z-txt' data-bind='value:title,click:$parent.edit.clickTab' style='width:90%'/></td>
334. </tr>
335. </tbody>
336. </table>
337. </div> 
338.  
339. <div id='edit-tab-setting' style='float:left;overflow:auto;' data-bind='autoheight:172,autowidth:355,visible:edit.selectedTitle()!=null'>
340.  
341. </div>
342. </div>
343.  
344. <div id='step-3' class='step'>
345. <h4 class='StepTitle'>第三步 其它设置</h4>
346.  
347. <div class='container_12'>
348. <div class='grid_1 lbl'>业务区域</div>
349. <div class='grid_2 val'><input type='text' class='z-txt' data-bind='value:form.area'/></div>
350.  
351. <div class='clear'></div>
352.  
353. <div class='grid_1 lbl'>控制器名称</div>
354. <div class='grid_2 val'><input type='text' class='z-txt' data-bind='value:form.controller'/></div>
355.  
356. <div class='clear'></div>
357.  
358. <div class='grid_1 lbl'>生成路径</div>
359. <div class='grid_2 val'><input type='text' class='z-txt' data-bind='value:form.path'/></div>
360. </div>
361. </div>
362. </script>
363.  
364. <script type='text/html' id='template-edit-tab-setting'>
365. <div style='padding:8px;clear:both'>
366. <span>页签类型 </span>
367. <select class='z-txt' style='padding:0;height:22px;' data-bind='value:edit.selectedTab.type'>
368. <option value='empty'>empty</option>
369. <option value='grid'>grid</option>
370. <option value='form'>form</option>
371. </select>
372.  
373. <span data-bind='visible:edit.selectedTab.type()!='empty''> 数据表 </span>
374. <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>
375.  
376. <span data-bind='visible:edit.selectedTab.type()!='empty''>与主表的关联</span>
377. <select class='z-txt' style='padding:0;height:22px;' data-bind='options:data.tablekey,value:edit.selectedTab.relationship,visible:edit.selectedTab.type()!='empty''></select>
378. </div>
379.  
380. <div style='width:180px;float:left;overflow:auto;margin-right:-18px;' data-bind='autoheight:212,visible:edit.selectedTab.type()!='empty''>
381. <ul data-bind='easyuiTree:edit.columntree2'></ul>
382. </div>
383.  
384. <div style='float:right;overflow:auto;' data-bind='autoheight:210,autowidth:535,visible:edit.selectedTab.type()!='empty''>
385. <table class='grid'>
386. <thead>
387. <tr>
388. <th style='width:50px'>字段</th>
389. <th style='width:100px'>题头</th>
390. <th style='width:30px' data-bind='visible:edit.selectedTab.type()=='grid''>隐藏</th>
391. <th style='width:30px' data-bind='visible:edit.selectedTab.type()=='grid''>排序</th>
392. <th style='width:50px' data-bind='visible:edit.selectedTab.type()=='grid''>对齐</th>
393. <th style='width:40px' data-bind='visible:edit.selectedTab.type()=='grid''>宽度</th>
394. <th style='width:50px' data-bind='visible:edit.selectedTab.type()=='grid''>格式化</th>
395. <th style='width:50px' data-bind='visible:edit.selectedTab.type()=='grid''>编辑器</th>
396. <th style='width:50px' data-bind='visible:edit.selectedTab.type()=='form''>控件类型</th>
397. <th style='width:10px' data-bind='visible:edit.selectedTab.type()=='form''>只读</th>
398. </tr>
399. </thead>
400. <tbody data-bind='foreach:edit.selectedTab.columns'>
401. <tr data-bind='attr:{id:$index}'>
402. <td data-bind='text:field' style='text-align:left'></td>
403. <td><input type='text' class='z-txt' data-bind='value:title' /></td>
404. <td data-bind='visible:$parent.edit.selectedTab.type()=='grid''><input type='checkbox' data-bind='checked:hidden'/></td>
405. <td data-bind='visible:$parent.edit.selectedTab.type()=='grid''><input type='checkbox' data-bind='checked:sortable'/></td>
406. <td data-bind='visible:$parent.edit.selectedTab.type()=='grid''><select class='z-txt'  data-bind='options:$root.data.align,value:align' ></select></td>
407. <td data-bind='visible:$parent.edit.selectedTab.type()=='grid''><input type='text' class='z-txt' data-bind='value:width' /></td>
408. <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>
409. <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>
410. <td data-bind='visible:$parent.edit.selectedTab.type()=='form''><select class='z-txt'  data-bind='options:$root.data.input,value:type'></select></td>
411. <td data-bind='visible:$parent.edit.selectedTab.type()=='form''><input type='checkbox' data-bind='checked:readonly'/></td>
412. </tr>
413. </tbody>
414. </table>
415. </div>
416. </script>

Generator.js

view source print ?
001. /**
002. * 模块名:mms viewModel
003. * 程序名: Generator.js
004. * Copyright(c) 2013 liuhuisheng [ liuhuisheng.xm@gmail.com ]
005. **/
006.  
007. var viewModel = function () {
008. var self = this;
009.  
010. this.form = {
011. type: '',
012. database:ko.observable(),
013. table: ko.observable(),
014. controller: ko.observable(),
015. area:ko.observable(),
016. conditions: ko.observableArray(),
017. columns: ko.observableArray(),
018. tabs: ko.observableArray(),
019. path: ko.observable('~/Generator/')
020. };
021.  
022. this.resetForm = function () {
023. self.form.conditions([]);
024. self.form.columns([]);
025. self.form.tabs([]);
026. };
027.  
028. this.data = {
029. codetype: [{ text: 'search', value: 'search' }, { text: 'edit', value: 'edit' }, { text: 'searchEdit', value: 'searchEdit' }],
030. database: ko.observableArray(),
031. table: ko.observableArray(),
032. column:ko.observableArray(),
033. tablekey: ko.observableArray(),
034. input: ['text', 'autocomplete', 'combobox', 'lookup','datebox','daterange'],
035. compare: ['equal', 'like', 'startwith', 'endwith', 'greater', 'less', 'daterange'],
036. align:['left','center','right'],
037. formatter: [{text:'',value:''},{ text: '日期', value: 'com.formatDate' }, { text: '时间', value: 'com.formatTime' }, { text: '金额', value: 'com.formatMoney' }, { text: '是否', value: 'com.formatCheckbox' }],
038. 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' }]
039. };
040.  
041. this.initDatabase = function () {
042. com.ajax({
043. type: 'GET',
044. async:false,
045. url: '/api/sys/generator/GetConnectionStrings',
046. success: function (d) {
047. self.data.database(d);
048. }
049. });
050. };
051.  
052. this.initDatabase();
053.  
054. this.getTableUrl = function () {
055. return '/api/sys/generator/GetTables?database=' + self.form.database();
056. };
057. this.getColumnUrl = function (table) {
058. return '/api/sys/generator/GetColumns?database=' + self.form.database() + '&table=' + table;
059. }
060.  
061. this.codetype = {
062. showblank: true,
063. width: 110,
064. data: self.data.codetype,
065. onSelect: function (node) {
066. self.form.type = node.value;
067. self.initWizard();
068. }
069. };
070.  
071. this.database = {
072. showblank: true,
073. width: 110,
074. data: self.data.database,
075. onSelect: function (node) {
076. self.form.database(node.value)
077. self.form.area((node.value.split('.')[1] || node.value).replace(/(^|s+)w/g, function (s) { return s.toUpperCase(); }));
078. }
079. };
080.  
081. this.tabletree = {
082. method: 'GET',
083. url: ko.computed(self.getTableUrl),
084. loadFilter: function (d) {
085. var data = utils.filterProperties(d.rows || d, ['TableName as id', 'TableName as text']);
086. self.data.table(data);
087. return data;
088. },
089. onSelect: function (node) {
090. self.form.table(node.id);
091. self.edit.init();
092. self.resetWizard();
093. self.form.controller((node.id.split('_')[1] || node.id).replace(/(^|s+)w/g, function (s) { return s.toUpperCase(); }));
094. }
095. };
096.  
097. this.generator = function () {
098. com.ajax({
099. type:'POST',
100. url: '/api/sys/generator',
101. data: ko.toJSON(self.form),
102. success: function (d) {
103. com.message('success', '代码已生成!');
104. }
105. });
106. };
107.  
108. this.searchEdit = {};
109. this.searchEdit.columntree = {
110. method: 'GET',
111. url: ko.computed(function () {
112. return self.getColumnUrl(self.form.table());
113. }),
114. checkbox: true,
115. loadFilter: function (d) {
116. return utils.filterProperties(d.rows || d, ['ColumnName as id', 'ColumnName as text']);
117. },
118. onSelect: function (node) {
119. var handle = node.checked ? 'uncheck' : 'check';
120. $(this).tree(handle, node.target);
121. },
122. onCheck: function (node, checked) {
123. if (checked)
124. self.form.conditions.push({ field: node.id, title: node.id, type: 'text', options: '', cp: 'equal',readonly:false });
125. else
126. self.form.conditions.remove(function (item) { return item.field == node.id });
127. },
128. onLoadSuccess: self.resetForm
129. };
130.  
131. this.searchEdit.columntree2 = {
132. method: 'GET',
133. url: ko.computed(function () {
134. return self.getColumnUrl(self.form.table());
135. }),
136. checkbox: true,
137. loadFilter: function (d) {
138. return utils.filterProperties(d.rows || d, ['ColumnName as id', 'ColumnName as text']);
139. },
140. onSelect: function (node) {
141. var handle = node.checked ? 'uncheck' : 'check';
142. $(this).tree(handle, node.target);
143. },
144. onCheck: function (node, checked) {
145. var arr = self.form.columns;
146.  
147. if (checked) {
148. var item = $.grep(arr(), function (row) {return row.field == node.id;})[0];
149. item || arr.push({ field: node.id, title: node.id, hidden: false, sortable: true, align: 'left', width: 80, formatter: '', editor: 'text' });
150. } else
151. arr.remove(function (item) { return item.field == node.id });
152. }
153. };
154.  
155. this.edit = {};
156. this.edit.selectedTab = {
157. title: ko.observable(),
158. type: ko.observable(),
159. subtable: ko.observable(),
160. relationship: ko.observable(),
161. columns: ko.observableArray(),
162. primaryKeys:ko.observableArray()
163. };
164.  
165. this.edit.columntree2 = {
166. method: 'GET',
167. url:ko.observable(),
168. checkbox: true,
169. loadFilter: function (d) {
170. self.data.column(d);
171. var list = utils.filterProperties(d.rows || d, ['ColumnName as id', 'ColumnName as text']);
172. self.edit.setDefaultForm();
173. self.edit.resetTableKey();
174. var checkedList = [];
175. for (var i in self.edit.selectedTab.columns())
176. checkedList.push(self.edit.selectedTab.columns()[i].field);
177. for (var i in list)
178. if ($.inArray(list[i].id, checkedList) > -1) list[i].checked = true;
179.  
180. return list
181. },
182. onSelect: function (node) {
183. var handle = node.checked ? 'uncheck' : 'check';
184. $(this).tree(handle, node.target);
185. },
186. onCheck: function (node, checked) {
187. var arr = self.edit.selectedTab.columns;
188.  
189. if (checked) {
190. var item = $.grep(arr(), function (row) { return row.field == node.id; })[0];
191. item || arr.push({ field: node.id, title: node.id, hidden: false, sortable: true, align: 'left', width: 80, formatter: '', editor: 'text', type: '', readonly: true });
192. } else
193. arr.remove(function (item) { return item.field == node.id });
194. }
195. }
196. this.edit.init = function () {
197. self.edit.selectedTitle(null);
198. self.edit.selectedTab = null;
199. $('#edit-tab-setting').empty();
200. };
201. this.edit.addTab = function () {
202. var title = 'tab' + (self.form.tabs().length + 1);
203. var newTab = {
204. title: ko.observable(title),
205. type: ko.observable('empty'),
206. subtable: ko.observable(self.form.table()),
207. relationship: ko.observable(),
208. columns: ko.observableArray(),
209. primaryKeys:ko.observableArray()
210. };
211. newTab.type.subscribe(function (value) {
212. if (value == 'grid') {
213. var item = $.grep(self.data.table(), function (row) { return row.id == self.form.table() + 'Detail' })[0];
214. if (item)
215. newTab.subtable(item.id);
216. }
217. else if (value == 'form') {
218. newTab.subtable(self.form.table());
219. }
220. });
221. newTab.columns.subscribe(self.tableDnDUpdate);
222. newTab.subtable.subscribe(function (value) {
223. self.edit.selectedTab.columns([]);
224. self.edit.columntree2.url(self.getColumnUrl(value));
225. });
226.  
227. self.form.tabs.push(newTab);
228. };
229.  
230. this.edit.removeTab = function (row,event) {
231. self.form.tabs.remove(row);
232.  
233. if (row.title() == self.edit.selectedTitle())
234. self.edit.selectedTitle(null);
235. };
236. this.edit.selectedTitle = ko.observable();
237. this.edit.clickTab = function (row, event) {
238. if (row.title() == self.edit.selectedTitle()) return;
239.  
240. self.edit.selectedTitle(row.title());
241. self.edit.selectedTab = row;
242. self.edit.columntree2.url = ko.observable(self.getColumnUrl(self.edit.selectedTab.subtable()));
243.  
244. var currentTr = $(event.srcElement).parent('td').parent('tr');
245. currentTr.parent().find('tr.tree-node-selected').removeClass('tree-node-selected');
246. currentTr.addClass('tree-node-selected');
247.  
248. var tabTemplate = $('#template-edit-tab-setting').html();
249. var wrapper = $('#edit-tab-setting').empty().html(tabTemplate);
250.  
251. ko.cleanNode(wrapper[0]);
252. ko.applyBindings(self, wrapper[0]);
253. wrapper.find('table').tableDnD({ onDrop: self.tableDnDSort });
254. };
255. this.edit.resetTableKey = function () {
256. var relationship = self.edit.selectedTab.relationship();
257. self.data.tablekey([]);
258. var cols = self.data.column();
259. for (var i in cols)
260. if (cols[i].IsIdentity || cols[i].IsPrimaryKey)
261. self.data.tablekey.push(cols[i].ColumnName);
262.  
263. self.edit.selectedTab.relationship(relationship);
264. self.edit.selectedTab.primaryKeys(self.data.tablekey());
265. };
266. this.edit.setDefaultForm = function () {
267. var arr = [
268. { field: 'ApproveState', title: '审批状态', type: 'text', readonly: true },
269. { field: 'ApproveRemark', title: '审批意见', type: 'text', readonly: true },
270. { field: 'ApprovePerson', title: '审批人', type: 'text', readonly: true },
271. { field: 'ApproveDate', title: '审批日期', type: 'datebox', readonly: true },
272. { field: 'CreatePerson', title: '编制人', type: 'text', readonly: true },
273. { field: 'CreateDate', title: '编制日期', type: 'datebox', readonly: true },
274. { field: 'UpdatePerson', title: '修改人', type: 'text', readonly: true },
275. { field: 'UpdateDate', title: '修改日期', type: 'datebox', readonly: true }
276. ];
277.  
278. var cols = self.data.column();
279. var defaults = { field: '', title: '', hidden: false, sortable: true, align: 'left', width: 80, formatter: '', editor: 'text', type: '', readonly: true };
280. for (var i in arr) {
281. if (!$.grep(cols, function (item) { return item.ColumnName == arr[i].field; }).length)
282. return;
283.  
284. arr[i] = $.extend({}, defaults, arr[i]);
285. }
286.  
287. self.edit.selectedTab.columns(arr);
288.  
289. var tree = self.edit.columntree2.$element();
290. for (var i in arr) {
291. var node = tree.tree('find', arr[i].field);
292. if (node) tree.tree('check', node.target);
293. }
294. };
295.  
296. this.initWizard = function () {
297. var stepTemplate = $('#template-' + self.form.type);
298. if (!stepTemplate.length) return;
299.  
300. var wizard = $('#wizard').removeData('smartWizard').empty();
301. ko.cleanNode(wizard[0]);
302.  
303. wizard.html(stepTemplate.html());
304. wizard.smartWizard({
305. labelNext: '下一步',
306. labelPrevious: '上一步',
307. labelFinish: '生成',
308. onFinish: self.generator
309. });
310. var resizeStep = function () {
311. $('.step').height($(window).height() - 145)
312. .width($(window).width() - 205);
313. $('.actionBar').width($(window).width() - 195);
314. var index = wizard.smartWizard('currentStep');
315. wizard.smartWizard('goToStep', index);
316. };
317. $(window).resize(resizeStep);
318. resizeStep();
319. ko.applyBindings(self, wizard[0]);
320. wizard.find('table').tableDnD({ onDrop: self.tableDnDSort });
321.  
322. for (var i in self.form) {
323. if ($.isFunction(self.form[i]))
324. if (self.form[i]() instanceof Array)
325. if (self.form[i].subscribe)
326. self.form[i].subscribe(self.tableDnDUpdate);
327. }
328. };
329. this.resetWizard = function () {
330. var wizard = $('#wizard').smartWizard('goToStep', 1);
331. for (var i = 1; i <= wizard.find('>ul>li').length; i++)
332. wizard.smartWizard('disableStep', i);
333. };
334.  
335. this.tableDnDUpdate = function () {
336. setTimeout('$('table').tableDnDUpdate()', 300);
337. };
338.  
339. this.tableDnDSort = function (table, row) {
340. var name = $(table).find('tbody').attr('data-bind').replace('foreach:form.','');
341. var array = self.form[name], i = 0;
342.  
343. if (name == 'foreach:edit.selectedTab.columns')
344. array = self.edit.selectedTab.columns;
345.  
346. $('tr[id]', table).each(function () { array()[this.id].id = i++; });
347. array.sort(function (left, right) { return left.id == right.id ? 0 : (left.id < right.id ? -1 : 1) });
348.  
349. //for fix ko bug refresh ui
350. var tempArr = array();
351. array([]);
352. array(tempArr);
353. };
354. };

razor模板
model.cshtml

view source print ?
01. @using Zephyr.Core.Generator
02. using System;
03. using System.Collections.Generic;
04. using System.Text;
05. using Zephyr.Core;
06.  
07. namespace Zephyr.Web.@(@Model.Area).Models
08. {
09. [Module('@Model.Database')]
10. public class @(Model.TableName)Service : ServiceBase<@Model.TableName>
11. {
12.  
13. }
14.  
15. public class @Model.TableName : ModelBase
16. {
17. @foreach(TableSchema item in Model.Columns)
18. {
19. if (item.IsIdentity)
20. {
21. @:[Identity]
22. }
23.  
24. if (item.IsPrimaryKey)
25. {
26. @:[PrimaryKey]  
27. }
28. @:public @item.TypeName @item.ColumnName { get; set; }
29. }
30. }
31. }

其它各页面的模板我就不一一贴出来了,大家可以查看我以前的那些关于共通viewModel的博客,查询及编辑页面我都有详细介绍过,大家也可以自己提炼出自己的一套模板,然后也可以用这同一个思路去做一个代码生成器。

七、后述

有了这个代码生成的功能,5分钟做一个基本的页面应该是完全没有问题的。
我这里分享下自己的代码生成的思路,权当抛砖引玉,大家有什么更好的方法欢迎留言。
如果大家感兴趣,就在右下角帮我【推荐】一下吧,谢谢大家了。

这篇关于快速开发之代码生成器(asp.net mvc4+easyui+knockoutjs)的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/242527

相关文章

基于Qt开发一个简单的OFD阅读器

《基于Qt开发一个简单的OFD阅读器》这篇文章主要为大家详细介绍了如何使用Qt框架开发一个功能强大且性能优异的OFD阅读器,文中的示例代码讲解详细,有需要的小伙伴可以参考一下... 目录摘要引言一、OFD文件格式解析二、文档结构解析三、页面渲染四、用户交互五、性能优化六、示例代码七、未来发展方向八、结论摘要

Rust中的Option枚举快速入门教程

《Rust中的Option枚举快速入门教程》Rust中的Option枚举用于表示可能不存在的值,提供了多种方法来处理这些值,避免了空指针异常,文章介绍了Option的定义、常见方法、使用场景以及注意事... 目录引言Option介绍Option的常见方法Option使用场景场景一:函数返回可能不存在的值场景

在 VSCode 中配置 C++ 开发环境的详细教程

《在VSCode中配置C++开发环境的详细教程》本文详细介绍了如何在VisualStudioCode(VSCode)中配置C++开发环境,包括安装必要的工具、配置编译器、设置调试环境等步骤,通... 目录如何在 VSCode 中配置 C++ 开发环境:详细教程1. 什么是 VSCode?2. 安装 VSCo

C#图表开发之Chart详解

《C#图表开发之Chart详解》C#中的Chart控件用于开发图表功能,具有Series和ChartArea两个重要属性,Series属性是SeriesCollection类型,包含多个Series对... 目录OverviChina编程ewSeries类总结OverviewC#中,开发图表功能的控件是Char

鸿蒙开发搭建flutter适配的开发环境

《鸿蒙开发搭建flutter适配的开发环境》文章详细介绍了在Windows系统上如何创建和运行鸿蒙Flutter项目,包括使用flutterdoctor检测环境、创建项目、编译HAP包以及在真机上运... 目录环境搭建创建运行项目打包项目总结环境搭建1.安装 DevEco Studio NEXT IDE

Python开发围棋游戏的实例代码(实现全部功能)

《Python开发围棋游戏的实例代码(实现全部功能)》围棋是一种古老而复杂的策略棋类游戏,起源于中国,已有超过2500年的历史,本文介绍了如何用Python开发一个简单的围棋游戏,实例代码涵盖了游戏的... 目录1. 围棋游戏概述1.1 游戏规则1.2 游戏设计思路2. 环境准备3. 创建棋盘3.1 棋盘类

.NET利用C#字节流动态操作Excel文件

《.NET利用C#字节流动态操作Excel文件》在.NET开发中,通过字节流动态操作Excel文件提供了一种高效且灵活的方式处理数据,本文将演示如何在.NET平台使用C#通过字节流创建,读取,编辑及保... 目录用C#创建并保存Excel工作簿为字节流用C#通过字节流直接读取Excel文件数据用C#通过字节

这15个Vue指令,让你的项目开发爽到爆

1. V-Hotkey 仓库地址: github.com/Dafrok/v-ho… Demo: 戳这里 https://dafrok.github.io/v-hotkey 安装: npm install --save v-hotkey 这个指令可以给组件绑定一个或多个快捷键。你想要通过按下 Escape 键后隐藏某个组件,按住 Control 和回车键再显示它吗?小菜一碟: <template

Hadoop企业开发案例调优场景

需求 (1)需求:从1G数据中,统计每个单词出现次数。服务器3台,每台配置4G内存,4核CPU,4线程。 (2)需求分析: 1G / 128m = 8个MapTask;1个ReduceTask;1个mrAppMaster 平均每个节点运行10个 / 3台 ≈ 3个任务(4    3    3) HDFS参数调优 (1)修改:hadoop-env.sh export HDFS_NAMENOD

电脑桌面文件删除了怎么找回来?别急,快速恢复攻略在此

在日常使用电脑的过程中,我们经常会遇到这样的情况:一不小心,桌面上的某个重要文件被删除了。这时,大多数人可能会感到惊慌失措,不知所措。 其实,不必过于担心,因为有很多方法可以帮助我们找回被删除的桌面文件。下面,就让我们一起来了解一下这些恢复桌面文件的方法吧。 一、使用撤销操作 如果我们刚刚删除了桌面上的文件,并且还没有进行其他操作,那么可以尝试使用撤销操作来恢复文件。在键盘上同时按下“C