在上一篇"在ASP.NET MVC4中實現同頁面增刪改查,無彈出框01,Repository的搭建"中,已經搭建好了Repository層,本篇就剩下增刪改查的界面了......今天的陽光真特么好,寫完本篇,好出去在陽光下溜溜狗、散散步什么的,正所謂文武之道一張一弛,走神了,進入正題。
首先是一個View Model,在這里定義驗證規則,提交和保存數據的時候還必須和領域模型映射。
using System;using System.ComponentModel.DataAnnotations;namespace MvcApplication3.Models{public class ProductVm{public int Id { get; set; }[Required(ErrorMessage = "必填")][Display(Name = "名稱")][StringLength(6, ErrorMessage = "最大長度6位")]public string Name { get; set; }[Required(ErrorMessage = "必填")][Display(Name = "分類")][StringLength(6, ErrorMessage = "最大長度6位")]public string Category { get; set; }[Required(ErrorMessage = "必填")][Display(Name = "價格")][Range(typeof(Decimal), "0", "9999", ErrorMessage = "{0} 必須是數字介於 {1} 和 {2}之間.")]public decimal Price { get; set; }}}
創建HomeController
using System.Linq;using System.Web.Mvc;using MvcApplication3.Models;namespace MvcApplication3.Controllers{public class ProductController : Controller{static readonly IProductRepository repository = new ProductRepository();#region 顯示查詢/// <summary>/// 顯示主界面/// </summary>/// <returns></returns>public ActionResult Index(){return View();}private string _name = string.Empty; //用來接收查詢中有關Name的值private string _category = string.Empty;//用來接收查詢中有關Category的值/// <summary>/// 根據查詢參數獲取所有產品,以json返回/// </summary>/// <returns></returns>public ActionResult GetProdutJson(){//page和rows是datagrid傳遞的默認參數int pageIndex = int.Parse(Request["page"]);int pageSize = int.Parse(Request["rows"]);//如果查詢中包括Nameif (!string.IsNullOrEmpty(Request["name"])){_name = Request["name"];}//如果查詢中包括Categoryif (!string.IsNullOrEmpty(Request["category"])){_category = Request["category"];}//初始化查詢實例var queryParams = new ProductParam{PageIndex = pageIndex,PageSize = pageSize,Name = _name,Category = _category};//根據查詢實例查找對應的數據int totalNum = 0;var products = repository.LoadProductPageData(queryParams, out totalNum);//投影出需要傳遞給datagrid的數據var result = from p in productsselect new {p.Id, p.Name, p.Category, p.Price};//datagrid所需要的格式{total:10, rows=[]}var jsonResult = new {total = totalNum, rows = result};return Json(jsonResult, JsonRequestBehavior.AllowGet);}#endregion#region 添加/// <summary>/// 添加顯示,返回一個強類型部分視圖/// </summary>/// <returns></returns>public ActionResult AddProduct(){return PartialView("_AddProduct", new ProductVm());}/// <summary>/// 添加提交/// </summary>/// <returns></returns>[HttpPost][ValidateAntiForgeryToken]public ActionResult AddProduct(ProductVm productVm){if (ModelState.IsValid){Product dbProduct = new Product();dbProduct.Name = productVm.Name;dbProduct.Category = productVm.Category;dbProduct.Price = productVm.Price;repository.Add(dbProduct);return Json(new { msg = true });}else{return PartialView("_AddProduct", new ProductVm());}}#endregion#region 修改/// <summary>/// 修改顯示/// </summary>/// <returns></returns>public ActionResult EditProduct(int id){var dbProduct = repository.GetById(id);ProductVm productVm = new ProductVm();productVm.Id = dbProduct.Id;productVm.Name = dbProduct.Name;productVm.Category = dbProduct.Category;productVm.Price = dbProduct.Price;return PartialView("_EditProduct", productVm);}/// <summary>/// 修改提交/// </summary>/// <param name="productVm"></param>/// <returns></returns>[HttpPost][ValidateAntiForgeryToken]public ActionResult EditProduct(ProductVm productVm){if (ModelState.IsValid){var dbProduct = repository.GetById(productVm.Id);dbProduct.Name = productVm.Name;dbProduct.Category = productVm.Category;dbProduct.Price = productVm.Price;repository.Update(dbProduct);return Json(new { msg = true });}else{return PartialView("_EditProduct", productVm);}}#endregion#region 刪除/// <summary>/// 刪除/// </summary>/// <returns></returns>[HttpPost]public ActionResult DeleteProducts(){//獲取前台傳來的Id字符串var strIds = Request["ids"];//去除最后一位分隔符strIds = strIds.Substring(0, strIds.Length - 1);//把字符串轉換成數組string[] ids = strIds.Split('_');repository.DeleteBatch(ids);return Json(new { msg = true });}#endregion}}
在前台頁面會使用jQuery EasyUI的datagrid顯示數據和分頁,而datagrid在向服務端發送異步請求的時候默認帶了page和rows這2個參數。GetProdutJson方法用來獲取查詢、分頁后的json格式。在前台,當頁面初次加載的時候,不帶任何查詢數據,服務端只要接收page和rows這2個參數,把它們封裝成一個類。
namespace MvcApplication3.Models{public class PageParam{public int PageSize { get; set; }public int PageIndex { get; set; }}}
而當在前台輸入搜索條件的時候,搜索條件參數以及page和rows這2個參數都傳值給了服務端,為此我們再封裝一個派生於PageParam的類。
namespace MvcApplication3.Models{public class ProductParam : PageParam{public string Name { get; set; }public string Category { get; set; }}}
所以,在GetProdutJson方法內,最終是把從前台接收到的分頁參數或查詢參數,實例化成ProductParam的一個實例,再把該實例作為實參傳值給Repository的LoadProductPageData方法。
主界面Home/Index.cshtml
進入主界面Home/Index.cshtml之前,先設置_Layout.cshtml。
<!DOCTYPE html><html><head><meta charset="utf-8" /><meta name="viewport" content="width=device-width" /><title>@ViewBag.Title</title>@Styles.Render("~/Content/css")<link href="~/bootstrap/css/bootstrap.min.css" rel="stylesheet" /><link href="~/Content/themes/icon.css" rel="stylesheet" /><link href="~/Content/themes/gray/easyui.css" rel="stylesheet" />@RenderSection("styles", required:false)@Scripts.Render("~/bundles/modernizr")@Scripts.Render("~/bundles/jquery")@Scripts.Render("~/bundles/jqueryui")@Scripts.Render("~/bundles/jqueryval")<script src="~/bootstrap/js/bootstrap.min.js"></script></head><body>@RenderBody()@RenderSection("scripts", required: false)</body></html>
用到了bootstrap, jQuery EasyUI, jQuery等相關的css,js文件,該有的要有,順序要放對。
Product/Index.cshtml需要呈現的包括:
○ 添加區域:放在一個div中,div中再放一個iframe,需要的時候通過iframe鏈接到添加頁面
○ 修改區域:放在一個div中,div中再放一個iframe,需要的時候通過iframe鏈接到修改頁面
○ 搜索區域:使用bootstrap顯示元素
○ 列表區域:使用datagrid顯示,支持分頁
Product/Index.cshtml需要承擔的工作包括:
○ 頁面加載的時候隱藏添加和修改區域
○ 限制搜索區域有關名稱和類別的長度
○ 頁面加載顯示全部數據列表
○ 點擊搜索按鈕顯示符合搜素條件的數據列表
○ 點擊清空按鈕讓搜索條件清空
○ datagrid添加:讓添加區域內的iframe指向ProductController的AddProduct方法,渲染強類型添加視圖
○ datagrid修改:讓修改區域內的iframe指向ProductController的EditProduct方法,渲染強類型修改視圖,並傳遞當前Product的Id
○ datagrid刪除:支持批量刪除,把勾選行所在的Id拼接成"1_2_"的形式傳遞給PrductContrlller的DeleteProduct方法,返回json
○ 提供給子視圖頁面調用的添加成功、添加取消、修改成功、修改取消方法,Home/Index.cshtml為父頁面,添加和修改區域內的iframe指向的視圖頁面是子頁面
@{ViewBag.Title = "Index";Layout = "~/Views/Shared/_Layout.cshtml";}<!--添加開始--><div class="addContent" id="addContent"><iframe id="frameAdd" src="" scrolling="yes" frameborder="0" height="100%" width="100%" onload="this.height=this.contentWindow.document.documentElement.scrollHeight"></iframe></div><!--添加結束--><!--編輯開始--><div class="editContent" id="editContent"><iframe id="frameEdit" src="" scrolling="yes" frameborder="0" height="100%" width="100%" onload="this.height=this.contentWindow.document.documentElement.scrollHeight"></iframe></div><!--編輯結束--><!--搜索開始--><div id="tb" style="padding: 3px"><form class="form-inline" role="form"><div class="form-group"><label class="sr-only" for="name">名稱</label><input type="text" class="form-control" name="name" id="name" placeholder="輸入名稱" maxlength="6" /></div><div class="form-group"><label class="sr-only" for="category">分類</label><input type="text" class="form-control" name="category" id="category" placeholder="輸入分類" maxlength="6" /></div><input type="button" id="btnSearch" class="btn btn-primary" value="搜索" /> <input type="button" id="emptySearch" class="btn btn-default" value="清空" /></form></div><!--搜索結束—><!--表格開始--><div class="ctable" id="ctable"><table id="tt"></table></div><!--表格結束-->@section scripts{<script src="~/Scripts/jquery.easyui.min.js"></script><script src="~/Scripts/easyui-lang-zh_CN.js"></script><script type="text/javascript">$(function() {//隱藏元素initialHide();//限制搜索條件中名稱和分類的長度limitInputLength($('#name'));limitInputLength($('#category'));//顯示列表initData();//搜索$('#tb').on("click", "#btnSearch", function () {initData(initQuery());});//清空搜索$('#emptySearch').on("click", function () {emptySearch();});});//顯示列表function initData(params) {$('#tt').datagrid({url: '@Url.Action("GetProdutJson", "Product")',height: 360,width: 900,fitColumns: false,nowrap: true,showFooter: true,idField: 'ID',loadMsg: '正在加載信息...',pagination: true,singleSelect: false,queryParams: params,pageSize: 10,pageNumber: 1,pageList: [10, 20, 30],columns: [//p.Id, p.Name, p.Category, p.Price[{ field: 'ck', checkbox: true, align: 'center', width: 30 },{ field: 'Id', title: '編號', align: 'center' },{ field: 'Name', title: '名稱', align: 'center' },{ field: 'Price', title: '價格', align: 'center' },{ field: 'Category', title: '分類', align: 'center' }]],toolbar: [{id: 'btnAdd',text: '添加',iconCls: 'icon-add',handler: function () {showAdd();}}, '-', {id: 'btnUpdate',text: '修改',iconCls: 'icon-edit',handler: function () {var rows = $('#tt').datagrid("getSelections");if (rows.length != 1) {$.messager.alert("提示", "只能選擇一行進行編輯");return;}showEdit(rows[0].Id);}}, '-', {id: 'btnDelete',text: '刪除',iconCls: 'icon-remove',handler: function () {var rows = $('#tt').datagrid("getSelections");if (rows.length < 1) {$.messager.alert("提示", "請選一行刪除");return;}$.messager.confirm("提示信息", "確定要刪除嗎?", function (r) {if (r) {var strIds = "";for (var i = 0; i < rows.length; i++) {strIds += rows[i].Id + '_'; //1_2_3}$.post("@Url.Action("DeleteProducts", "Product")", { ids: strIds }, function (data) {if (data.msg) {$.messager.alert("提示", "刪除成功");initData();$('#tt').datagrid("clearSelections");}});}});}}],OnBeforeLoad: function (param) {return true;}});}//添加function showAdd() {$("#frameAdd").attr("src", "@Url.Action("AddProduct", "Product")");$("#addContent").css("display", "block");}//修改function showEdit(productId) {var url = "/Product/EditProduct?id=" + productId;$("#frameEdit").attr("src", url);$('.addContent').css("display", "none");$("#editContent").css("display", "block");}//隱藏元素function initialHide() {$('.addContent').css("display", "none");$('.editContent').css("display", "none");}//限制文本框長度function limitInputLength(_input) {var _max = _input.attr('maxlength');_input.bind('keyup change', function () {if ($(this).val().length > _max) {($(this).val($(this).val().substring(0, _max)));}});}//子窗口添加成功后調用function refreshAfterAdd() {initData();$('#tt').datagrid("clearSelections");$("#addContent").css("display", "none");}//子窗口取消添加調用function cancelAdd() {$('.addContent').css("display", "none");$('#tt').datagrid("clearSelections");}//子窗口修改成功后調用function refreshAfterEdit() {initData();$('#tt').datagrid("clearSelections");$("#editContent").css("display", "none");}//子窗口取消修改調用function candelEdit() {$("#editContent").css("display", "none");$('#tt').datagrid("clearSelections");}//獲取查詢表單的值組成jsonfunction initQuery() {var queryParams = {name: $('#name').val(),category: $('#category').val()};return queryParams;}//清空搜索條件function emptySearch() {$('#name').val("");$('#category').val("");}</script>}
添加強類型視圖頁Product/_AddProduct.cshtml
是一個針對ProductVm強類型視圖。添加成功或取消都會調用父視圖提供的方法。
@model MvcApplication3.Models.ProductVm@{ViewBag.Title = "_AddProduct";Layout = "~/Views/Shared/_Layout.cshtml";}@section styles{<style type="text/css">.errormsg {color: red;}.vcenter {display: table-cell;vertical-align: middle;float: none;border: 0px solid red;height: 38px;}.bk {background-color: #F8F8F8;padding: 2px;border-radius: 5px;}form {width: 900px;/*height: 450px;*/}.control-label {position: relative;top: 5px;}.col-sm-6 {height: 40px;}.col-sm-2 {height: 40px;}.col-sm-4 {height: 40px;}#wrapper {width: 550px;clear: both;float: left;margin-top: 10px;}</style>}<div id="wrapper">@using (Html.BeginForm("AddProduct", "Product", FormMethod.Post, new { id = "addForm", @class = "form-horizontal", role = "form" })){@Html.AntiForgeryToken()<div class="form-group">@Html.LabelFor(s => s.Name,new { @class="col-sm-2 control-label" })<div class="col-sm-6">@Html.TextBoxFor(s => s.Name,new { @class="form-control input-sm"}) </div><div class="col-sm-4 vcenter">@Html.ValidationMessageFor(s => s.Name)</div></div><div class="form-group bk">@Html.LabelFor(s => s.Price,new { @class="col-sm-2 control-label" })<div class="col-sm-6">@Html.TextBoxFor(s => s.Price,new { @class="form-control input-sm"}) </div><div class="col-sm-4 vcenter">@Html.ValidationMessageFor(s => s.Price)</div></div><div class="form-group">@Html.LabelFor(s => s.Category,new { @class="col-sm-2 control-label" })<div class="col-sm-6">@Html.TextBoxFor(s => s.Category,new { @class="form-control input-sm"}) </div><div class="col-sm-4 vcenter">@Html.ValidationMessageFor(s => s.Category)</div></div><div class="form-group bk"><div style="text-align: center;" class="col-sm-6 col-sm-offset-2"><input type="button" id="up" class="btn btn-primary" value="添加"/> <input type="button" id="cancel" class="btn btn-default" value="取消"/></div></div>}</div>@section scripts{<script type="text/javascript">$(function() {//提交$('#up').on('click', function () {if ($('#addForm').valid()) {$.ajax({cache: false,url: '@Url.Action("AddProduct","Product")',type: 'POST',dataType: 'json',data: $('#addForm').serialize(),success: function (data) {if (data.msg) {$('input[type=text]').val("");self.parent.refreshAfterAdd();}},error: function (xhr, status) {alert("添加失敗,狀態碼:" + status);}});}});//點擊取消按鈕關閉窗口$('#cancel').on('click', function () {$('input[type=text]').val("");self.parent.cancelAdd();});});</script>}
修改強類型視圖頁Product/_EditProduct.cshtml
是一個針對ProductVm強類型視圖。修改成功或取消都會調用父視圖提供的方法。
@model MvcApplication3.Models.ProductVm@{ViewBag.Title = "_EditProduct";Layout = "~/Views/Shared/_Layout.cshtml";}@section styles{<style type="text/css">.errormsg {color: red;}.vcenter {display: table-cell;vertical-align: middle;float: none;border: 0px solid red;height: 38px;}.bk {background-color: #F8F8F8;padding: 2px;border-radius: 5px;}form {width: 900px;/*height: 450px;*/}.control-label {position: relative;top: 5px;}.col-sm-6 {height: 40px;}.col-sm-2 {height: 40px;}.col-sm-4 {height: 40px;}#wrapper {width: 550px;clear: both;float: left;margin-top: 10px;}</style>}<div id="wrapper">@using (Html.BeginForm("EditProduct", "Product", FormMethod.Post, new { id = "editForm", @class = "form-horizontal", role = "form" })){@Html.AntiForgeryToken()@Html.HiddenFor(s => s.Id)<div class="form-group">@Html.LabelFor(s => s.Name,new { @class="col-sm-2 control-label" })<div class="col-sm-6">@Html.TextBoxFor(s => s.Name,new { @class="form-control input-sm"}) </div><div class="col-sm-4 vcenter">@Html.ValidationMessageFor(s => s.Name)</div></div><div class="form-group bk">@Html.LabelFor(s => s.Price,new { @class="col-sm-2 control-label" })<div class="col-sm-6">@Html.TextBoxFor(s => s.Price,new { @class="form-control input-sm"}) </div><div class="col-sm-4 vcenter">@Html.ValidationMessageFor(s => s.Price)</div></div><div class="form-group">@Html.LabelFor(s => s.Category,new { @class="col-sm-2 control-label" })<div class="col-sm-6">@Html.TextBoxFor(s => s.Category,new { @class="form-control input-sm"}) </div><div class="col-sm-4 vcenter">@Html.ValidationMessageFor(s => s.Category)</div></div><div class="form-group bk"><div style="text-align: center;" class="col-sm-6 col-sm-offset-2"><input type="button" id="up" class="btn btn-primary" value="修改"/> <input type="button" id="cancel" class="btn btn-default" value="取消"/></div></div>}</div>@section scripts{<script type="text/javascript">$(function() {//提交$('#up').on('click', function () {if ($('#editForm').valid()) {$.ajax({cache: false,url: '@Url.Action("EditProduct","Product")',type: 'POST',dataType: 'json',data: $('#editForm').serialize(),success: function (data) {if (data.msg) {self.parent.refreshAfterEdit();}},error: function (xhr, status) {alert("修改失敗,狀態碼:" + status);}});}});//點擊取消按鈕關閉窗口$('#cancel').on('click', function () {self.parent.candelEdit();});});</script>}




