Layout的设计

模板模式

mvc的模板特别类似设计模式中模板方法模式,结合Layout中RenderSection和RenderBody方法可以将部分html展现逻辑延迟到具体的视图页面去实现里面实现。结合我们增删改查的逻辑,我们的用户界面,我们将页面分为这几个区域,实现部分逻辑以后,部分留给具体的页面去实现。例如图片中新增,编辑,删除,导入,导出,查询都是架构自带的操作,至于复制就给页面扩展,查询条件也留给具体的页面中扩展,模板中给出RenderSection即可。

执行顺序

这个执行顺序的问题可以通过调试去查找答案,大致是返回页面后,先执行具体页面的逻辑,当第一个@{} 代码段执行完毕之后,会检查有没有Layout页面,如果有则进入Layout顺序执行,如果Layout页面有RenderSection则会去字页面检查有没有定义,如果定义则执行,页面中除了RenderSection部分都会当作RenderBody中执行,但是实际程序运行中,页面会全部动态编译成dll去执行。具体可以查看Razor引擎模板的相关内容。

元数据

mvc定义了一套元数据机制,在模板中可以通过ViewData.ModelMatedata去访问这样可以搭配attribute做出很多扩展的设计,具体案例会在本文后面做详细说明。

列表布局页

@using Coralcode.Framework.Mvc.Models.MiniUI
@using HCC.Web.Utils
@{
Layout = null;
//如果不使用父类默认设定操作按钮,请重新制定参数
if (ViewBag.DataOperation == null)
{
ViewBag.DataOperation =
MiniUIDataOperation.Add | MiniUIDataOperation.Edit | MiniUIDataOperation.Delete
| MiniUIDataOperation.ExportTemplate | MiniUIDataOperation.Import | MiniUIDataOperation.Export;
}
MiniUIDataOperation dataOperation = (MiniUIDataOperation)ViewBag.DataOperation; if (ViewBag.AddEditDialogHeight == null)
{
ViewBag.AddEditDialogHeight = 600;
}
if (ViewBag.AddEditDialogWidth == null)
{
ViewBag.AddEditDialogWidth = 800;
}
string refreshUrl=ViewBag.ListUrl;
if (ViewBag.ShowPager)
{
refreshUrl = ViewBag.PageUrl;
} }
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" content="width=device-width" />
<title></title>
<meta http-equiv="content-type" content="text/Html; charset=UTF-8" />
@Styles.Render("~/content/miniuicss")
@Scripts.Render("~/script/miniuijs", "~/script/tooljs")
</head>
<body>
<style type="text/css">
html, body {
margin: 0;
padding: 0;
border: 0;
width: 100%;
height: 100%;
overflow: hidden;
}
</style>
<div id="datagrid_toolbar">
<div class="mini-toolbar" style="border-bottom: 0; padding: 0px;">
<table style="width: 100%;">
<tr>
<td style="width: 100%;">
<div id="operation">
@if ((dataOperation & MiniUIDataOperation.Add) != 0)
{
<a class="mini-button" iconcls="icon-add" onclick="add()">增加</a>
}
@if ((dataOperation & MiniUIDataOperation.Edit) != 0)
{
<a class="mini-button" iconcls="icon-edit" onclick="edit()">编辑</a>
}
@if ((dataOperation & MiniUIDataOperation.Delete) != 0)
{
<a class="mini-button" iconcls="icon-remove" onclick="remove()">删除</a>
}
@if ((dataOperation & MiniUIDataOperation.ExportTemplate) != 0)
{
<a class="mini-button" iconcls="icon-download" href='@ViewBag.ExportTemplateUrl'>导出模板</a>
}
@if ((dataOperation & MiniUIDataOperation.Import) != 0)
{
<a class="mini-button" iconcls="icon-upload" onclick="importAction()">导入</a>
}
@if ((dataOperation & MiniUIDataOperation.Export) != 0)
{
<a class="mini-button" iconcls="icon-download" href='@ViewBag.ExportUrl'>导出</a>
}
@if ((dataOperation & MiniUIDataOperation.Register) != 0)
{
<a class="mini-button" iconcls="icon-user" onclick="RegisterLesson()">预约</a>
}
@if ((dataOperation & MiniUIDataOperation.Sign) != 0)
{
<a class="mini-button" iconcls="icon-tip" onclick='SignLesson()'>签到</a>
}
@if ((dataOperation & MiniUIDataOperation.InputUser) != 0)
{
string currentMember = string.Empty;
var userIdName = UserHelper.GetCookieIdName();
if (userIdName != null)
{
currentMember = string.Format("(当前'{0}')",userIdName.Item2);
} <a class="mini-button" iconcls="icon-user" id="a_userName" onclick='InputUserMemberView()'>获取会员@{@currentMember}</a>
}
@RenderSection("ExtendedOperation", false)
</div>
</td>
<td style="white-space: nowrap;">
<div id="query">
@RenderSection("Query", false)
<a class="mini-button" iconcls="icon-search" onclick="LoadData()">查询</a> </div>
</td>
</tr>
</table>
</div>
</div>
<!--撑满页面-->
<div class="mini-fit">
<div id="datagrid1" class="mini-datagrid" style="width: 100%; height: 100%;" showpager="@ViewBag.ShowPager" idfield="id" multiselect="true">
<div property="columns">
@Html.Partial("_HeaderPartial", (List<DataGridColumn>)ViewBag.Header)
</div>
</div>
</div>
@RenderBody() @* ReSharper disable once SyntaxIsNotAllowed *@
<script type="text/javascript">
mini.parse();
var grid = mini.get("datagrid1");
var url = '@Html.Raw(ViewBag.EditUrl)';
var importUrl = '@Html.Raw(ViewBag.ImportUrl)';
var title = '@ViewBag.Title'; grid.set({
url: "@Html.Raw(refreshUrl)",
pageSize:20,
pageIndex:10,
sizeList:[10,20,50,100]
}); function LoadData() {
var data="{" + $("#query input[name]").map(function() {
return $(this).attr("name") + ":" + "'"+$(this).val()+"'";
}).get().join(", ")+"}";
grid.load(mini.decode(data,false));
} //todo 这里如果需要查询怎么办
LoadData(); var dynamicUrlParams = ""; @if ((dataOperation & MiniUIDataOperation.Add) != 0)
{
<text>
function add() {
mini.open({
url: url, title: "新增" + title,
width: @ViewBag.AddEditDialogWidth,
height: @ViewBag.AddEditDialogHeight,
onload: function() {
var iframe = this.getIFrameEl();
var data = { action: "new" };
//iframe.contentWindow.SetData(data);
},
ondestroy: function(action) { grid.reload();
}
});
}
</text>
}
@if ((dataOperation & MiniUIDataOperation.Edit) != 0)
{
<text>
function edit() { var row = grid.getSelected();
if (row) {
var editUrl = url + (url.indexOf('?') > 0 ? '&' : '?') + "id=" + row.Id; mini.open({
url: editUrl,
title: "编辑" + title,
width: @ViewBag.AddEditDialogWidth,
height: @ViewBag.AddEditDialogHeight, ondestroy: function(action) {
grid.reload(); }
}); } else {
alert("请选中一条记录");
} }
</text>
}
@if ((dataOperation & MiniUIDataOperation.Delete) != 0)
{
<text>
function remove() { var rows = grid.getSelecteds(); if (rows.length > 0) {
if (confirm("确定删除选中记录?")) {
var ids = [];
for (var i = 0, l = rows.length; i < l; i++) {
var r = rows[i];
ids.push(r.Id);
}
var id = ids.join(','); grid.loading("操作中,请稍后......");
var deleteUrl = '@Html.Raw(ViewBag.DeleteUrl)';
deleteUrl = deleteUrl + (deleteUrl.indexOf('?') > 0 ? '&' : '?') + "ids=" + id;
$.ajax({
type: 'post',
url: deleteUrl,
success: function(data) {
if (data.State == 0) {
grid.reload();
return;
}
grid.unmask();
alert(data.Data); },
error: function() {
}
});
}
} else {
alert("请选中一条记录");
}
}
</text>
}
@if ((dataOperation & MiniUIDataOperation.Import) != 0)
{
<text>
function importAction() {
mini.open({ url: importUrl,
title: "导入" + title,
width: 600,
height: 300, ondestroy: function(action) {
grid.reload(); }
});
}
</text>
} @if ((dataOperation & MiniUIDataOperation.Register) != 0)
{
<text>
function RegisterLesson() {
var rows = grid.getSelecteds();
if (rows.length < 1) {
alert("请选中一条记录");
return;
}
if (rows.length > 1) {
alert("每次只能预订一节课程");
return;
}
var ids = [];
for (var i = 0, l = rows.length; i < l; i++) {
var r = rows[i];
ids.push(r.Id);
}
var id = ids.join(','); grid.loading("操作中,请稍后......");
var registerLessonUrl = '@Html.Raw(ViewBag.RegisterLessonUrl)';
registerLessonUrl = registerLessonUrl
+ (registerLessonUrl.indexOf('?') > 0 ? '&' : '?') + "ids=" + id
+ "&"+dynamicUrlParams;
$.ajax({
type: 'post',
url: registerLessonUrl,
success: function (data) {
if (data.State == 0) {
grid.reload();
return;
}
grid.unmask();
alert(data.Data); },
error: function () {
}
}); } </text>
}
@if ((dataOperation & MiniUIDataOperation.Sign) != 0)
{
<text>
</text>
} @if ((dataOperation & MiniUIDataOperation.InputUser) != 0)
{
<text> function InputUserMemberView(){
mini.prompt("请输入会员号码:", "请输入",
function (action, value) {
if (action != "ok") {
return;
}
var userLoginUrl = '/Portal/User/Login?number='+value+ "&"+dynamicUrlParams;
$.ajax({
type: 'post',
url: userLoginUrl,
success: function (data) {
if (data.State == 0) {
$("#a_userName").children("span").html("获取会员(当前'"+data.Data+"')")
return;
}
alert(data.Data); },
error: function () {
}
}); }
);
} </text>
} function onDateRenderer(e) {
var value = e.value;
if (value) return mini.formatDate(value, 'yyyy-MM-dd');
return "";
}
function onDateTimeRenderer(e) {
var value = e.value;
if (value) return mini.formatDate(value, 'yyyy-MM-dd hh:mm:dd');
return "";
}
function onTimeRenderer(e) {
var value = e.value;
if (value) return mini.formatDate(value, 'hh:mm:dd');
return "";
}
</script>
@RenderSection("Script", false)
</body>
</html>

操作配置化,并且可扩展

MVC的模板加载顺序,基于mvc的模板顺序,我们可以在自模板中定义出页面操作,如果子页面没有定义,则在模板中给出默认定义。

Flags的枚举 由于页面操作是可以交叉的,所以我们采用flags的枚举来作为判断依据,flags枚举其实就是二进制的位操作,建议没用过的好好去研究下。在很多地方都很有用,特别是条件组合的情况下。

流式布局

结合RennderSection的设计,模板并不知道操作和查询条件有多少,所以我们做成流式布局,操作左对齐,查询右对齐,在少数操作和少数查询的时候效果还不错,

新增编辑布局页

@{
Layout = null;
}
<!DOCTYPE html>
<html>
<head>
<meta name="viewport" />
<title></title>
<meta http-equiv="content-type" content="text/Html; charset=UTF-8" />
<script type="text/javascript" src="@Url.Content("~/Content/js/jquery-1.6.2.min.js")"></script>
<script type="text/javascript" src="@Url.Content("~/Content/js/boot.js")"></script>
@Styles.Render("~/content/miniuicss")
@Scripts.Render("~/script/miniuijs")
@Scripts.Render("~/script/tooljs")
<link href="@Url.Content("~/Content/css/edit.css")" type="text/css" rel="stylesheet"/> <script type="text/javascript" src="@Url.Content("~/Content/js/jquery.form.js")"></script>
</head>
<body style="margin: 0px;height: 100%"> <form method="post" id="formmain" style="width: 100%; height: 100%">
<div class="mini-fit" style="padding: 5px"> <table style="table-layout: fixed; width: 100%;">
@RenderBody()
</table>
</div> <div class="mini-toolbar" style="text-align:center;padding-top:8px;padding-bottom:8px; " borderStyle="border:1;"> <a id="submit" class="mini-button" iconcls="icon-ok" onclick="submit()">确定</a>
<span style="display: inline-block; width: 25px;"></span>
<a id="cancel" class="mini-button" iconcls="icon-cancel" onclick="onCancel(this)">取消</a>
</div> </form>
@Scripts.Render("~/script/jquery.validate")
<script type="text/javascript"> function submit() {
$.ajax({
type: "Post",
data:$("form").serialize(),
success: function (jsonResult) { if (jsonResult.State === 0) {
CloseWindow("save");
return;
}
alert(jsonResult.Data);
}
});
}; function onCancel(e) {
CloseWindow("cancel");
}
function CloseWindow(action) {
if (window.CloseOwnerWindow) window.CloseOwnerWindow(action);
else window.close();
}
</script>
@RenderSection("Script", false)
</body>
</html>

界面尽量精简

编辑模板操作ajax提交的方式,将提交逻辑封装在模板页中,子页面只需要填充输入字段即可这里要特别提醒,尽量做到编辑界面的简洁,将那些复杂的输入作为编辑模板扩展,这部分将在下一节中说明。

自适应的布局

布局中,将操作放在最下方提供提交和取消两个操作即可满足要求。剩下与输入相关的部分可以在具体页面中去做扩展。最终我们是要实现Html.EditForModel的扩展,暂时精力有限没有做到这一步,最后在更精简的设计中看是否有时间去实现这个逻辑。当然由于部分页面输入逻辑比较复杂,所以即使做出来了,也不能替代全部的编辑页面。

MVC编辑模板

挖掘MVC源代码地图

我们查看mvc的源代码可以发现,mvc结合DataType提供了编辑和显示两种模板类型。放在Views/Shared的EditTemplate和DisplayTemplate中,同时在模板中也可以获取到对应模型属性的元数据,这里要特别注意,不要给html.textbox这类输入方法中提供name属性,mvc自己会在name中默认拼接上元数据中的属性名。掌握这种方式之后可以做在编辑页面做出更简洁的设计,如果你扩展的类型够多,那么将会给程序员编程提供很大的便利.

富文本编辑

我们选用DataType.MultilineText作为富文本的扩展,将ViewModel属性标记[DataType(DataType.MultilineText)],然后再编辑的时候只需Html.EditFor()即可实现自动调用,我们选用UEditer作为复文本框,只需简单几行代码就可以实现富文本框的扩展。

@model string
@Scripts.Render("~/script/ueditor")
@Styles.Render("~/content/ueditor")
@Html.TextArea("", Model, new { id = ViewData.ModelMetadata.PropertyName })
<script type="text/javascript">
var editor = new baidu.editor.ui.Editor();
editor.render('@ViewData.ModelMetadata.PropertyName');
</script>

时间相关扩展

在mvc的DataType的枚举中有DateTime,Date,Time三种和时间相关的类型,这里用mini ui自带的时间控件做的扩展,使用的时候只需要结合属性的DateTypeAttribute和
Html.EditFor即可。

DateTime扩展

@model DateTime
@{
DateTime value = DateTime.Now;
if (Model != DateTime.MinValue)
{
value=Model ;
}
}
@Html.TextBox("", value, new { @class = "mini-datepicker", format = "yyyy-MM-dd H:mm:ss", timeFormat = "H:mm:ss", showTime = "true" ,width=165})

Date扩展

@model DateTime
@{
DateTime value = DateTime.Now;
if (Model != DateTime.MinValue)
{
value = Model;
}
}
@Html.TextBox("", value, new { @class = "mini-datepicker", format = "yyyy-MM-dd", showTime = "false" })

Time扩展

@model DateTime
@{
DateTime value = DateTime.Now;
if (Model != DateTime.MinValue)
{
value = Model;
}
}
@Html.TextBox(ViewData.ModelMetadata.PropertyName, value, new { @class = "mini-timespinner", format = "H:mm:ss" })

菜单的设计

菜单的数据结构

首先看看整体的压面布局,分为顶栏,左边菜单,右边内容,底部关于这种布局。上边部分就很简单,给出用户信息和登出操作就好了。这里我使用miniui自带的树形控件做的菜单,结合样式miniui自带样式就可以简洁的实现如图的效果

菜单加载就用miniui的tree就很方便了,用Identify和ParentId作为父子关系即可,其中parent属性忽略掉。

var tree = mini.get("leftTree");
tree.loadList(data.Data, "Identify", "ParentId");
两行js语句即可 public class MenuModel : IViewModel
{
/// <summary>
/// 标识
/// </summary>
public string Identify { get; set; } /// <summary>
/// 标题
/// </summary>
public string Text { get; set; } /// <summary>
/// 父节点
/// </summary>
[JsonIgnore]
public MenuModel Parent { get; set; } /// <summary>
/// 父节点标识
/// </summary>
public string ParentId { get; set; } /// <summary>
/// 链接
/// </summary>
public string Url { get; set; } /// <summary>
/// 等级
/// </summary>
public int Level
{
get
{
if (Parent == null)
return 0;
return Parent.Level + 1;
}
} /// <summary>
/// 子节点
/// </summary>
public List<MenuModel> Children { get; set; } public long Id { get; set; }
}

菜单的服务

using System.Collections.Generic;
using HCC.Core.ViewModel; namespace HCC.Core.Services
{
public interface IMenuService
{
/// <summary>
/// 根节点
/// </summary>
MenuModel Root { get; } /// <summary>
/// 菜单注册
/// </summary>
/// <param name="menuName">菜单名称</param>
/// <param name="url">页面地址</param>
/// <param name="parentId">上级菜单</param>
/// <param name="menuId">菜单Id</param>
MenuModel Regist(string menuName, string url, string parentId, string menuId); /// <summary>
/// 菜单注册
/// </summary>
/// <param name="menuName">菜单名称</param>
/// <param name="url">页面地址</param>
/// <param name="parentId">上级菜单</param>
/// <param name="menuId">菜单Id</param>
MenuModel Regist(string menuName, string url, string parentId); /// <summary>
/// 注销菜单
/// </summary>
/// <param name="menuId"></param>
/// <returns></returns>
MenuModel Unregist(string menuId); /// <summary>
/// 根据菜单ID获取菜单
/// </summary>
/// <param name="menuId"></param>
/// <returns></returns>
MenuModel FindById(string menuId); /// <summary>
/// 获取子菜单
/// </summary>
/// <returns></returns>
List<MenuModel> GetChildrenMenus(string parentId); /// <summary>
/// 根据级别获取菜单
/// </summary>
/// <returns></returns>
List<MenuModel> GetByLevel(List<int> levels); /// <summary>
/// 获取全部菜单
/// </summary>
/// <returns></returns>
List<MenuModel> GetAll(); /// <summary>
/// 重新注册菜单
/// </summary>
void ResetMenu();
}
}

注意其中菜单服务这里,可以订阅eventbus事件来动态更新菜单

using System.Collections.Generic;
using System.Linq;
using Coralcode.Framework.Aspect;
using Coralcode.Framework.Data.Repository.Core;
using Coralcode.Framework.Data.Specification;
using Coralcode.Framework.Mapper;
using Coralcode.Framework.MessageBus.Event;
using Coralcode.Framework.Services;
using Coralcode.Framework.Utils;
using HCC.Core.Model;
using HCC.Core.ViewModel; namespace HCC.Core.Services.Imp
{
[Inject(RegisterType = typeof (IMenuService), LifetimeManagerType = LifetimeManagerType.ContainerControlled)]
public class MenuService : CrudCoralService<Menu, MenuModel>, IMenuService
{
internal static Dictionary<string, MenuModel> MenuCache = new Dictionary<string, MenuModel>(); public MenuService(IRepository<Menu> repository, IEventBus eventBus)
: base(repository, eventBus)
{
Root = new MenuModel
{
Identify = "root",
Text = "后台管理",
Url = "",
Children = new List<MenuModel>(),
Parent = null
};
MenuCache.Add(Root.Identify, Root);
var allMenus = Repository.GetAll().ToList();
allMenus.ForEach(item => { Regist(item.Text, item.Url, item.ParentId, item.Identify); });
} /// <summary>
/// 根节点
/// </summary>
public MenuModel Root { get; private set; } public MenuModel Regist(string menuName, string url, string parentId, string menuId)
{
if (MenuCache.ContainsKey(menuId))
return MenuCache[menuId];
if (string.IsNullOrEmpty(parentId) || !MenuCache.ContainsKey(parentId))
return null; var menu = new MenuModel
{
Identify = menuId,
Text = menuName,
Url = url,
Children = new List<MenuModel>(),
ParentId = parentId,
Parent = MenuCache[parentId]
}; AddMenu(menu);
MenuCache.Add(menu.Identify, menu);
MenuCache[parentId].Children.Add(menu);
return menu;
} public MenuModel Regist(string menuName, string url, string parentId)
{
return Regist(menuName, url, parentId, GeneralMenuId(parentId, menuName));
} public MenuModel Unregist(string menuId)
{
MenuModel model;
if (!MenuCache.TryGetValue(menuId, out model))
return null;
model.Parent.Children.Remove(model);
UnregistChildren(model);
Repository.UnitOfWork.Commit();
return model;
} public MenuModel FindById(string menuId)
{
return !MenuCache.ContainsKey(menuId) ? null : MenuCache[menuId];
} public List<MenuModel> GetChildrenMenus(string parentId)
{
if (string.IsNullOrEmpty(parentId))
return DataMapperProvider.Mapper.Convert<List<MenuModel>, List<MenuModel>>(Root.Children);
if (MenuCache.ContainsKey(parentId))
return DataMapperProvider.Mapper.Convert<List<MenuModel>, List<MenuModel>>(MenuCache[parentId].Children);
return null;
} public List<MenuModel> GetByLevel(List<int> levels)
{
return MenuCache.Values.Where(item => levels.Contains(item.Level)).ToList();
} /// <summary>
/// 重新注册菜单
/// </summary>
public void ResetMenu()
{
//删掉之前的菜单
var menus = Repository.GetAll().ToList();
if (menus.Count != 0)
{
menus.ForEach(item => { Repository.Remove(item); });
Repository.UnitOfWork.CommitAndRefreshChanges();
MenuCache.Clear();
MenuCache.Add(Root.Identify, Root);
} //课程管理
var lessonMenu = Regist("课程管理", "", "root", "lessonmanager");
Regist("私教课", "/portal/privatelesson/index", lessonMenu.Identify, "privatelesson");
Regist("公开课", "/portal/publiclesson/index", lessonMenu.Identify, "publiclesson");
Regist("体验课", "/portal/triallesson/index", lessonMenu.Identify, "triallesson");
Regist("会员活动", "/portal/useractivity/index", lessonMenu.Identify, "useractivity"); //人员管理
var userManagerMenu = Regist("人员管理", "", "root", "personmanager"); Regist("教练", "/portal/coach/index", userManagerMenu.Identify, "coachmanager"); Regist("人员", "/portal/user/index", userManagerMenu.Identify, "usermanager");
//系统管理
var systemManagerMenu = Regist("系统管理", "", "root", "systemmanager"); Regist("管理员", "/portal/manager/index", systemManagerMenu.Identify, "manager");
Regist("新闻管理", "/portal/news/index", systemManagerMenu.Identify, "news");
Regist("留言管理", "/portal/message/index", systemManagerMenu.Identify, "message");
Regist("课程模板", "/portal/lessontemplate/index", systemManagerMenu.Identify, "lessontemplate");
Regist("系统设置", "/portal/systemsetting/index", systemManagerMenu.Identify, "systemsetting");
//统计查询
var stasticsMenu = Regist("统计", "", "root", "systemstastistics");
Regist("统计", "/portal/statistic/index", stasticsMenu.Identify, "stastistics");
} private void UnregistChildren(MenuModel menu)
{
menu.Children.ForEach(item =>
{
if (MenuCache.ContainsKey(item.Identify))
RemoveMenu(item);
UnregistChildren(item);
});
RemoveMenu(menu);
} private void AddMenu(MenuModel menu)
{
if (MenuCache.ContainsKey(menu.Identify))
return;
var model = Repository.GetFirst(new DirectSpecification<Menu>(item => item.Identify == menu.Identify));
if (model != null)
return;
Repository.Add(Convert(menu));
Repository.UnitOfWork.Commit();
} private void RemoveMenu(MenuModel menu)
{
if (MenuCache.ContainsKey(menu.Identify))
MenuCache.Remove(menu.Identify);
var model = Repository.GetFirst(new DirectSpecification<Menu>(item => item.Identify == menu.Identify));
if (model == null)
return;
Repository.Remove(model);
} private string GeneralMenuId(params string[] keys)
{
return StringUtil.FormatKey(keys.ToArray());
}
}
}

菜单的缓存

菜单缓存采用和两种模式一种是根节点的树形模式,一种是字段缓存,这样既可以从树
去访问,也可以从字典根据key去访问。

Ps:界面代码比较乱,后面放出demo时候再整理,快了,由于最近确实比较忙,更新比较慢,见谅

CRUD全栈式编程架构之界面层的设计的更多相关文章

  1. CRUD全栈式编程架构之服务层的设计

    服务层代码 首先我先放出2个主要类的代码再分别讲解 接口 using System; using System.Collections.Generic; using System.Linq; usin ...

  2. CRUD全栈式编程架构之控制器的设计

    页面 这里界面我采用jquery miniui来做的,当你完全了解了整个设计之后可以轻松切换到其他的js框架,个人认为类似muniui,easyui等等这类可以将web界面做得和winform类似的框 ...

  3. CRUD全栈式编程架构之导入导出的设计

    using System; using System.Collections.Generic; using System.Linq; using System.Web; using System.We ...

  4. CRUD全栈式编程架构之更精简的设计

    精简的程度 ViewModel精简 服务精简 控制器精简 Index.cshmtl精简 AddOrEdit.cshtml精简 效果:最精简的情况下,只需要写Entity这一个数据库实体然后加上一些简单 ...

  5. CRUD全栈式编程架构之数据层的设计

    CodeFirst 一直以来我们写应用的时候首先都是创建数据库 终于在orm支持codefirst之后,我们可以先建模. 通过模型去创建数据库,并且基于codefirst可以实现方便的 实现数据库迁移 ...

  6. CRUD全栈式编程架构之MVC的扩展设计

    MVC执行流程 路由的扩展 我理解的路由作用有以下几个 Seo优化,用“/”分开的url爬虫更爱吃 物理和逻辑文件分离,url不再按照文件路径映射 Controller,Action的选择 MVC路由 ...

  7. CRUD全栈式编程架构总结

    这里放出实例代码 github.com/SkyvenXiong/HCC

  8. CRUD全栈式编程概述

    业务场景 CRUD,从数据驱动的角度几乎所有的的业务都是在做这样的事情.  几乎所有的操作都是在做对表的增删改查.  假设我们将数据库数据规个类:  分为基础/配置数据和业务/增长数据,或者说静态数据 ...

  9. 全栈式JavaScript

    如今,在创建一个Web应用的过程中,你需要做出许多架构方面的决策.当然,你会希望做的每一个决定都是正确的:你想要使用能够快速开发的技术,支持持续的迭代,最高的工作效率,迅速,健壮性强.你想要精益求精并 ...

随机推荐

  1. 5.监听器(Listener)

    1.监听器简介: 监听器主要用来监听对象的创建,属性的变化,是一个实现特定接口的普通Java类. Listener接口与事件对应表: 与 ServletContext 有关 ServletContex ...

  2. sql CET实现循环

    表结构 CREATE TABLE city( id INT IDENTITY(1,1) PRIMARY KEY, NAME NVARCHAR(100), ParentID INT , Parents ...

  3. 图像数据转换成db(leveldb/lmdb)文件(转)

    参考网站:http://www.cnblogs.com/denny402/p/5082341.html 在深度学习的实际应用中,我们经常用到的原始数据是图片文件,如jpg,jpeg,png,tif等格 ...

  4. vue中this.$router.push() 传参

    1  params 传参 注意⚠️:patams传参 ,路径不能使用path 只能使用name,不然获取不到传的数据 this.$router.push({name: 'dispatch', para ...

  5. java——变量

    1.静态变量: 随着类的加载而生成并初始化 随着类的消失而消失 2.成员变量: 随对象的加载而生成并初始化 随对象被回收而消失 3.局部变量: 作用范围由{}决定 随方法调用而创建 随方法的执行完毕而 ...

  6. c++11 多线程 1

    第3章 线程间共享数据 本章主要内容 共享数据带来的问题 使用互斥量保护数据 数据保护的替代方案 保护共享数据结构的最基本的方式,是使用C++标准库提供的互斥量(mutex). 清单3.1 使用互斥量 ...

  7. java编程--02日期格式化

    第一篇,介绍日期的比较 第二篇,介绍日期的格式化 第三篇,介绍关于日期常用的计算 第四篇,比较几个常用的日期时间相关类的区别 第五篇,jdk9对日期类进行了更新,写一些i自己的学习心得. 日期的格式化 ...

  8. 转:Android开源项目推荐之「网络请求哪家强」 Android开源项目推荐之「网络请求哪家强」

    转载自https://zhuanlan.zhihu.com/p/21879931 1. 原则 本篇说的网络请求专指 http 请求,在选择一个框架之前,我个人有个习惯,就是我喜欢选择专注的库,其实在软 ...

  9. mysql主从数据库错误处理

    方法一:忽略错误后,继续同步 该方法适用于主从库数据相差不大,或者要求数据可以不完全统一的情况,数据要求不严格的情况 解决: stop slave; #表示跳过一步错误,后面的数字可变set glob ...

  10. 将BufferedImage转换为InputStream,亲测可用

    private static final Logger logger = Logger.getLogger(Demo.class); /** * 将BufferedImage转换为InputStrea ...