纵有疾风起
人生不言弃

.Net MVC&&datatables.js&&bootstrap做一个界面的CRUD有多简单

    我们在项目开发中,做得最多的可能就是CRUD,那么我们如何在ASP.NET MVC中来做CRUD呢?如果说只是单纯实现功能,那自然是再简单不过了,可是我们要考虑如何来做得比较好维护比较好扩展,如何做得比较漂亮。做开发要有工匠精神,不要只求完成开发任务,那样的话,永远停留在只是简单的写业务逻辑代码水平,我们要做有追求的程序员。本来这么简单的东西,我真是懒得写,但是看到即便是一些工作了好些年的人,做东西也是只管实现功能,啥都不管,还有些界面css样式要么就硬编要么就毫无规则的在页面中进行穿插,遇到要设置间距甚至直接写多个 ,我觉得还是要写出来给那些人看下。硬编的前提是只有你这一个界面使用。

    我们先来看下我们要实现的效果,功能需要:新增、修改、删除、查询、分页、排序、导出excel、打印、上传图片、支持表单验证,优雅的实现有多简单?非常简单。

.Net MVC&&datatables.js&&bootstrap做一个界面的CRUD有多简单插图.Net MVC&&datatables.js&&bootstrap做一个界面的CRUD有多简单插图1

.Net MVC&&datatables.js&&bootstrap做一个界面的CRUD有多简单插图2.Net MVC&&datatables.js&&bootstrap做一个界面的CRUD有多简单插图3.Net MVC&&datatables.js&&bootstrap做一个界面的CRUD有多简单插图4.Net MVC&&datatables.js&&bootstrap做一个界面的CRUD有多简单插图5.Net MVC&&datatables.js&&bootstrap做一个界面的CRUD有多简单插图6

需要用到哪些UI组件?我都是基于bootstrap这种扁平化响应式风格的,jquery.dataTables.js、toastr.js、bootstrapValidator.js、bootstrap-confirmation.js、printThis.js

由于是演示用,所以控制器中直接调用了EF上下文操作,重点是看前端部分的渲染和交互,不要重复你的代码!不要重复你的代码!不要重复你的代码!重要的事情说三遍!为什么没有用mvc自带的模型验证?答:太傻逼!1、样式改起来操蛋;2、每次要点击提交表单才促发验证。

这里表数据量少,是直接一次性加载,然后内存中分页的,如果表数据量多,那么就要用服务器分页,同样很简单。修改下配置项,如下所示:然后控制器中对应的方法稍微修改下即可。

options.bServerSide = true;,
options.fnServerParams = function (aoData) { //查询条件 aoData.push( { "name": "LogName", "value": $("#LogName").val() } ); };

 BaseController控制器基类

.Net MVC&&datatables.js&&bootstrap做一个界面的CRUD有多简单插图7

    [PublicAuthorize]    public class BaseController : Controller    {        HomeService _HomeService = new HomeService();        #region 字段        /// <summary>        /// 新增        /// </summary>        protected string CText = "新增";        /// <summary>        /// 读取        /// </summary>        protected string RText = "读取";        /// <summary>        /// 更新        /// </summary>        protected string UText = "更新";        /// <summary>        /// 删除        /// </summary>        protected string DText = "删除";        /// <summary>        /// 数据有误!        /// </summary>        protected string VoidText = "数据有误!";        #endregion        #region 属性        /// <summary>        /// 获取点击的菜单ID        /// </summary>        public string MenuId { get { return Request.QueryString["MenuId"]; } }        /// <summary>        /// 自动构建页面标题导航        /// </summary>        public MvcHtmlString HeadString        {            get { return new MvcHtmlString(_HomeService.GetHead(int.Parse(MenuId))); }        }         /// <summary>        /// 构造非菜单页的界面导航标题        /// </summary>        /// <param name="title">页面标题</param>        public void CreateSubPageHead(string title)        {            ViewBag.HeadString = HeadString;            ViewBag.MenuId = MenuId;            ViewBag.SubHeadString = title;         }        #endregion        [HttpGet]        public virtual ActionResult Index()        {            if (!string.IsNullOrEmpty(MenuId))            {                ViewBag.MenuId = MenuId;                ViewBag.HeadString = HeadString;            }            return View();        }        /// <summary>        /// 操作成功        /// </summary>        /// <param name="message">提示文本</param>        /// <returns></returns>        protected virtual AjaxResult SuccessTip(string message)        {            return new AjaxResult { state = ResultType.success.ToString(), message = message };        }        /// <summary>        /// 操作失败        /// </summary>        /// <param name="message">提示文本</param>        /// <returns></returns>        protected virtual AjaxResult ErrorTip(string message)        {            return new AjaxResult { state = ResultType.error.ToString(), message = message };        }    }

View Code

控制器DefaultController

.Net MVC&&datatables.js&&bootstrap做一个界面的CRUD有多简单插图9

    public class DefaultController : BaseController    {        private MyContext db = new MyContext();        /// <summary>        /// 客户列表        /// </summary>        /// <param name="filter"></param>        /// <returns></returns>        [HttpPost]        public JsonResult List(Customer filter)        {            //filter.PageSize = int.MaxValue;            IQueryable<Customer> dataSource = db.Customers;            if (!string.IsNullOrEmpty(filter.Name))            {                dataSource = dataSource.Where(x => x.Name == filter.Name).OrderBy(x => x.CreateTime);            }            List<Customer> queryData = dataSource.ToList();            var data = queryData.Select(u => new            {                ID = u.Id,                Name = u.Name,                CreateTime = u.CreateTime.ToDateStr(),                Address = u.Address            });            //构造成Json的格式传递            var result = new { iTotalRecords = queryData.Count, iTotalDisplayRecords = 10, data = data };            return Json(result, JsonRequestBehavior.AllowGet);        }        #region CRUD        [HttpGet]        public ActionResult Create()        {            return View();        }        [HttpPost]        public JsonResult Create([Bind(Include = "Name,Address,CreateTime,Msg,HeadsUrl")] Customer _Customer)        {            AjaxResult _AjaxResult = null;            if (ModelState.IsValid)            {                db.Customers.Add(_Customer);                _AjaxResult = db.SaveChanges() > 0 ? SuccessTip(string.Format("{0}成功!", CText)) : ErrorTip(string.Format("{0}失败!", CText)); ;            }            else            {                _AjaxResult = ErrorTip(VoidText);            }            return Json(_AjaxResult, JsonRequestBehavior.AllowGet);        }        [HttpGet]        public ActionResult Update(int Id)        {            var model = db.Customers.Where(x => x.Id == Id).FirstOrDefault();            return View(model);        }        [HttpPost]        public JsonResult Update([Bind(Include = "Id,Name,Address,CreateTime,Msg,HeadsUrl")] Customer _Customer)        {            AjaxResult _AjaxResult = null;            if (ModelState.IsValid)            {                db.Entry(_Customer).State = EntityState.Modified;                _AjaxResult = db.SaveChanges() > 0 ? SuccessTip(string.Format("{0}成功!", UText)) : ErrorTip(string.Format("{0}失败!", UText));            }            else            {                _AjaxResult = ErrorTip(VoidText);            }            return Json(_AjaxResult, JsonRequestBehavior.AllowGet);        }        [HttpPost]        public JsonResult Delete(int Id)        {            var model = db.Customers.Where(x => x.Id == Id).FirstOrDefault();            db.Customers.Remove(model);            AjaxResult _AjaxResult = db.SaveChanges() > 0 ? SuccessTip(string.Format("{0}成功!", DText)) : ErrorTip(string.Format("{0}失败!", DText));            return Json(_AjaxResult, JsonRequestBehavior.AllowGet);        }        [HttpPost]        public JsonResult DeleteList(List<int> ids)        {            var list = db.Customers.Where(x => ids.Contains(x.Id)).ToList();            db.Customers.RemoveRange(list);            AjaxResult _AjaxResult = db.SaveChanges() > 0 ? SuccessTip(string.Format("{0}成功!", DText)) : ErrorTip(string.Format("{0}失败!", DText));            return Json(_AjaxResult, JsonRequestBehavior.AllowGet);        }        #endregion        #region File handle        /// <summary>        /// 导出Excel        /// </summary>        /// <returns></returns>        public FileResult ExportExcel()        {            string excelPath = Server.MapPath("~/Excel/用户列表.xls");            GenerateExcel genExcel = new GenerateExcel();            genExcel.SheetList.Add(new UserListSheet(db.Customers.ToList(), "用户列表"));            genExcel.ExportExcel(excelPath);            return File(excelPath, "application/ms-excel", "用户列表.xls");        }        /// <summary>        /// 上传文件        /// </summary>        /// <returns></returns>        public JsonResult ExportFile()        {            HttpPostedFileBase file = Request.Files["txt_file"];            uploadFile _uploadFile = new uploadFile();            if (file != null)            {                string str = DateTime.Now.ToString("yyyyMMddhhMMss");                var fileFullName =string.Format("{0}{1}_{2}",Request.MapPath("~/Upload/"),str ,file.FileName);                try                {                    file.SaveAs(fileFullName);                    _uploadFile.state = 1;                }                catch                {                    _uploadFile.state = 0;                }                finally                {                    _uploadFile.name = str+"_"+file.FileName;                    _uploadFile.fullName = fileFullName;                }            }            else            {                _uploadFile.state = 0;            }            return Json(_uploadFile, JsonRequestBehavior.AllowGet);        }        /// <summary>        /// 删除文件        /// </summary>        /// <returns></returns>        [HttpPost]        public JsonResult DeleteFile(string key)        {            var fileFullName = Path.Combine(Request.MapPath("~/Upload"), key);            int state = 0;            try            {                state = FileHelper.DeleteFile(fileFullName) ? 1 : 0;                //var model = db.Customers.Where(x => x.HeadsUrl == key).FirstOrDefault();                //if(model!=null)                //{                //    db.Customers.Remove(model);                //}            }            catch            {                state = 0;            }            return Json(state, JsonRequestBehavior.AllowGet);        }         #endregion    }

View Code

视图页面,razor、css、js都分离,不要放到一个文件中。

Index视图:

.Net MVC&&datatables.js&&bootstrap做一个界面的CRUD有多简单插图11

@model List<Secom.Smp.Data.Models.Customer>@{    ViewBag.Title = "用户列表";    ViewBag.ParentTitle = "系统管理";    Layout = "~/Views/Shared/_Page.cshtml";}<style type="text/css">    .divModal {        width: 700px;    }</style><div class="page-content-body">    <div class="row">        <div class="col-md-12">            <!-- BEGIN EXAMPLE TABLE PORTLET-->                <div class="portlet-title">                    <div class="caption font-dark">                        <i class="icon-settings font-dark"></i>                        <span class="caption-subject bold uppercase">用户列表</span>                    </div>                    <div class="actions">                                           </div>                </div>                <div class="portlet-body">                    <div class="table-toolbar">                        <div class="row">                            <div class="col-md-6">                                <div class="btn-group">                                    <button id="btnAdd" class="btn sbold green" onclick="DataTablesObj.doCreateModal('/Admin/Default/Create')" data-toggle="modal">添加用户<i class="fa fa-plus"></i></button>                                    <button id="btnDeleteList" title="确定要删除吗?" class="btn sbold btn-danger deleteBtn" data-toggle="confirmation" data-placement="right" data-btn-ok-label="继续" data-btn-ok-icon="icon-like" data-btn-ok-class="btn-success" data-btn-cancel-label="取消"                                            data-btn-cancel-icon="icon-close" data-btn-cancel-class="btn-danger">                                        批量删除                                        <i class="fa fa-minus"></i>                                    </button>                                </div>                            </div>                            <div class="col-md-6">                                <div class="btn-group pull-right">                                    <button class="btn green  btn-outline dropdown-toggle" data-toggle="dropdown">                                        操作                                        <i class="fa fa-angle-down"></i>                                    </button>                                    <ul class="dropdown-menu pull-right">                                        <li>                                            <a href="/Admin/Default/ExportExcel" target='_blank' class="fa fa-file-excel-o"><span style="margin-left:10px;"> 导出Excel </span></a>                                        </li>                                        <li>                                            <a href="#" class="fa fa-file-excel-o" id="printView"><span style="margin-left:10px;"> 打印预览</span></a>                                        </li>                                    </ul>                                </div>                            </div>                        </div>                    </div>                    <table class="table table-striped table-bordered table-hover table-checkable order-column" id="table_local"></table>                </div>            <!--模态弹窗-->            <div class="modal fade" id="defaultModal" tabindex="-1" role="dialog" aria-labelledby="defaultModalLabel" aria-hidden="true">                <div class="modal-dialog divModal" role="document">                    <div class="modal-content">                    </div>                </div>            </div>            <!-- END EXAMPLE TABLE PORTLET-->        </div>               </div></div>@section scripts{<script src="@Html.ScriptsPath("lib/printThis.js")"></script>}

View Code

Create视图:

.Net MVC&&datatables.js&&bootstrap做一个界面的CRUD有多简单插图13

@model Secom.Smp.Data.Models.Customer@{    ViewBag.Title = "Create";    Layout = "~/Views/Shared/_Form.cshtml";}<div class="modal-header">    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>    <h4 class="modal-title" id="defaultModalLabel">添加用户</h4></div>@using (Html.BeginForm("Create", "Default", new { area = "Admin" }, FormMethod.Post, new { @id = "defaultForm" }))            {    <div class="modal-body">        <div class="row">            <div class="col-md-5">                    <div class="form-group">                        @Html.LabelFor(x => x.HeadsUrl,new { @class = "control-label" }):                        <input type="file" id="txt_file" name="txt_file" class="file-loading" accept="image/*" />                        @Html.HiddenFor(x=>x.HeadsUrl,new { @id = "hidFileUrl" })                    </div>                </div>            <div class="col-md-7">                <div class="form-group">                    @Html.LabelFor(x => x.Name, new { @class = "control-label" }):                    @Html.TextBoxFor(x => x.Name, new { @id = "Name", @placeholder = "请输入用户名", @class = "form-control" })                </div>                <div class="form-group">                    @Html.LabelFor(x => x.Address, new { @class = "control-label" }):                    @Html.TextBoxFor(x => x.Address, new { @id = "Address", @placeholder = "请输入地址", @class = "form-control" })                </div>                <div class="form-group">                    @Html.LabelFor(x => x.CreateTime):                    <div class="input-group input-medium date date-picker">                        @Html.TextBoxFor(x => x.CreateTime, new { @class = "form-control", @readonly = true })                        <span class="input-group-btn">                            <button class="btn default" type="button">                                <i class="fa fa-calendar"></i>                            </button>                        </span>                    </div>                </div>            </div>        </div>    </div>    <div class="modal-footer">        <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>        <button type="submit" class="btn sbold green">提交</button>    </div>}@section scripts{}

View Code

Update视图:

.Net MVC&&datatables.js&&bootstrap做一个界面的CRUD有多简单插图15

@model Secom.Smp.Data.Models.Customer@{    ViewBag.Title = "Update";    Layout = "~/Views/Shared/_Form.cshtml";}<div class="modal-header">    <button type="button" class="close" data-dismiss="modal" aria-label="Close"><span aria-hidden="true">&times;</span></button>    <h4 class="modal-title" id="defaultModalLabel">修改用户</h4></div>    @using (Html.BeginForm("Update", "Default", new { area = "Admin" }, FormMethod.Post, new { @id = "updateForm" }))    {        @Html.HiddenFor(x=>x.Id)        <div class="modal-body">            <div class="row">                <div class="col-md-5">                    <div class="form-group">                        @Html.LabelFor(x => x.HeadsUrl, new { @class = "control-label" }):                        <input type="file" id="txt_file" name="txt_file" class="file-loading" accept="image/*" />                        @Html.HiddenFor(x => x.HeadsUrl, new { @id = "hidFileUrl" })                    </div>                </div>                <div class="col-md-7">                    <div class="form-group">                        @Html.LabelFor(x => x.Name, new { @class = "control-label" }):                        @Html.TextBoxFor(x => x.Name, new { @id = "Name", @placeholder = "请输入用户名", @class = "form-control" })                    </div>                    <div class="form-group">                        @Html.LabelFor(x => x.Address, new { @class = "control-label" }):                        @Html.TextBoxFor(x => x.Address, new { @id = "Address", @placeholder = "请输入地址", @class = "form-control" })                    </div>                    <div class="form-group">                        @Html.LabelFor(x => x.CreateTime):                        <div class="input-group input-medium date date-picker">                            @Html.TextBoxFor(model => model.CreateTime,           new { @type = "date", @class = "form-control", @readonly = true, @Value = Model.CreateTime.ToDateStr(),@id= "CreateTime" })                            <span class="input-group-btn">                                <button class="btn default" type="button">                                    <i class="fa fa-calendar"></i>                                </button>                            </span>                        </div>                    </div>                </div>            </div>        </div>        <div class="modal-footer">            <button type="button" class="btn btn-default" data-dismiss="modal">关闭</button>            <button type="submit" class="btn sbold green">提交</button>        </div>    } @section scripts{}

View Code

_Form模板页视图

<!DOCTYPE html><html><head>    <meta name="viewport" content="width=device-width" />    <title>@ViewBag.Title</title></head><body>    @RenderBody()    @RenderSection("scripts", required: false)            @Html.AutoLoadPageJs()</body></html>

看下这个自定义的扩展方法AutoLoadPageJs方法,其实就是模拟mvc的路由寻址方式去找指定目录下面的js

        /// <summary>        /// 根据mvc路由自动加载js文件(如果不存在则不加载)        /// </summary>        /// <param name="helper"></param>        /// <returns></returns>        public static MvcHtmlString AutoLoadPageJs(this HtmlHelper helper)        {            var areas = helper.ViewContext.RouteData.DataTokens["area"];            var action = helper.ViewContext.RouteData.Values["action"];            var controller = helper.ViewContext.RouteData.Values["controller"];            string url = areas == null ? string.Format("views/{0}/{1}", controller, action) : string.Format("views/areas/{2}/{0}/{1}", controller, action, areas);            return LoadJsString(helper,url);        }        /// <summary>        /// 构造js加载的html字符串        /// </summary>        /// <param name="helper"></param>        /// <param name="url">js文件路径</param>        /// <returns></returns>        public static MvcHtmlString LoadJsString(HtmlHelper helper, string url)        {            var jsBuilder = new StringBuilder();            string jsLocation = "/content/release-js/";#if DEBUG            jsLocation = "/content/js/";#endif            string jsFullUrl = Path.Combine(jsLocation, url + ".js");            if (File.Exists(helper.ViewContext.HttpContext.Server.MapPath(jsFullUrl)))            {                jsBuilder.AppendFormat("<script src=\"{0}\"></script>", jsFullUrl);            }            return new MvcHtmlString(jsBuilder.ToString());        }

我们看下View对应的js文件

.Net MVC&&datatables.js&&bootstrap做一个界面的CRUD有多简单插图17

index.js

$(function () {    //-------------初始化datatable    var obj = DataTablesObj;    obj.showReadBtn = false;//显示详情按钮    obj.showDeleteBtn = true;//显示删除按钮    obj.showUpdateBtn = true;//显示更新按钮    obj.updateUrl = "/Admin/Default/Update";    obj.deleteUrl = "/Admin/Default/Delete";    obj.batchDeleteUrl = "/Admin/Default/DeleteList";//批量删除路径    obj.options.columns = [{ title: "", "visible": false, "data": "ID" },           obj.checboxFied,           { "data": "Name", title: "用户名称" },           { "data": "Address", title: "用户地址" },           { "data": "CreateTime", title: "创建时间" },           obj.opratorFiled    ];    obj.options.searching = true;    obj.options.sAjaxSource = "/Admin/Default/List"; //数据源地址    obj.init(obj.options);    //表单验证配置项    FormValidatorObj.options.fields = {        Name: {            message: '用户名验证失败',            validators: {                notEmpty: {                    message: '用户名不能为空'                }            }        },        Address: {            validators: {                notEmpty: {                    message: '地址不能为空'                }            }        }    };    //打印预览    $("#printView").on("click", function () {        $("#table_local").printThis({            debug: false,// 调试模式下打印文本的渲染状态            importCSS: true,            importStyle: true,            printContainer: true,            //loadCSS: "/Content/bootstrap.css",            pageTitle: "用户列表",            removeInline: false,            printDelay: 333,            header: null,            formValues: true,            header: "<h1>用户列表</h1>",            footer: null        });    })})

create.js

$(function () {    DatetimepickerObj.init('CreateTime');//(控件ID)    //--------------表单验证    FormValidatorObj.init("defaultForm","defaultModal","table_local"); //(表单id,[modal容器Id],[datable容器ID])    //初始化编辑界面的数据    //--------------添加界面中的上传控件    FileInputObj.init(undefined,"txt_file", "hidFileUrl", "/Admin/Default/ExportFile",true);  //配置项,控件ID,存储文件路径的控件ID,上传路径,是否新增页面});

update.js

$(function () {    DatetimepickerObj.init('CreateTime');//(控件ID)    //--------------表单验证    FormValidatorObj.init("updateForm","defaultModal","table_local"); //(表单id,[modal容器Id],[datable容器ID])    //初始化编辑界面的数据    //([配置项],控件ID,存储文件路径的控件ID,图片路径,上传路径,删除路径)    FileInputObj.initUpdateImg(undefined,"txt_file", "hidFileUrl", "/Admin/Default/ExportFile", "/Admin/Default/DeleteFile");})

看上去比较多的界面功能,你看下,代码就这么点,而且各自职责很清晰。对datatables.js组件进行二次封装,base-Datatable.js代码如下:

.Net MVC&&datatables.js&&bootstrap做一个界面的CRUD有多简单插图18

// ajax加载调试用//# sourceURL=base-Datatable.js//DataTables表格组件扩展对象--created by zouqj 2017-7-03var DataTablesObj = (function () {    //------------------------------静态全局属性-------------------------------------    var infoStr = "总共 <span class='pagesStyle'>(_PAGES_) </span>页,显示 _START_ -- _END_ ,共<span class='recordsStyle'> (_TOTAL_)</span> 条";    var lengthMenuStr = '每页显示:<select class="form-control input-xsmall">' + '<option value="5">5</option>' + '<option value="10">10</option>' + '<option value="20">20</option>' + '<option value="30">30</option>'         + '<option value="50">50</option>' + '<option value="100">100</option>' + '<option value="150">150</option>' + '<option value="200">200</option>' + '<option value="250">250</option>' + '<option value="500">500</option>';    this.table_local = "table_local"; //table ID    this.chkAllColl = "chkAllColl"; //全选按钮ID    this.modalId = "defaultModal"; //模态窗体ID    this.batchDeleteBtn = "btnDeleteList"; //批量删除按钮ID    this.deleteBtn = "btnDelete"; //删除按钮ID    this.autoIncrement={'title': '序号', 'data': null, 'bSortable': false,'render': function (data, type, full, meta) {return meta.row + 1 + meta.settings._iDisplayStart;}}; // 序号    //操作列    this.opratorFiled = {        "data": "ID", orderable: false, title: "操作", "render": function (data, type, row, meta) { //自定义列            var re = "<div class='operatorDiv'></i>";            if (DataTablesObj.showReadBtn) {                var temp = DataTablesObj.detailModal == undefined ? data : data + ",'" + DataTablesObj.detailModal+"'";                re += "<a class='' type='button' data-toggle='dropdown' aria-expanded='false' onclick=\"DataTablesObj.doReadModal(" + temp + ")\">详情</a>";            }            if (DataTablesObj.showUpdateBtn) {                var temp = DataTablesObj.updateModal == undefined ? data : data + ",'" + DataTablesObj.updateModal + "'";                re += "<a class='' type='button' onclick='DataTablesObj.doUpdateModal(" + temp + ")'>编辑</a>";            }            if (DataTablesObj.showDeleteBtn) {                re += "<a class='' title='确定要删除吗?' data-toggle='confirmation' data-placement='left' data-btn-ok-label='继续' data-btn-ok-icon='icon-like' data-btn-ok-class='btn-success toastrBtn' data-btn-cancel-label='取消' data-btn-cancel-icon='icon-close' data-btn-cancel-class='btn-danger' type='button' onclick='DataTablesObj.doDelete(this," + data + ")'>删除</a>";            }            re += "</div>";            return re;        }    };    //复选框列    this.checboxFied = {        "data": "ID", title: "<input type='checkbox' id='chkAllColl' onclick='DataTablesObj.selectAll()'/>", orderable: false,        "render": function (data, type, row, meta) {            return "<input id='cbx" + data + "' name='chkItem' type='checkbox' onclick='DataTablesObj.controlSelectAll(" + data + ")' class='cbx' value='" + data + "'/>  ";        }    };    //------------------------------变化部分的属性-------------------------------------    this.batchDeleteUrl = "";//批量删除路径    this.deleteUrl = ""; //单条记录删除路径    this.showReadBtn = true;//默认不显示详情按钮    this.showDeleteBtn = false;//默认不显示删除按钮    this.showUpdateBtn = false;//默认显示更新按钮    this.readUrl = ""; //单条记录读取路径    this.updateUrl = "";//更新界面URL地址    this.detailModal = undefined; //详情页面的modalId    this.updateModal = undefined;//修改页面的modalId    //------------------------------事件、方法-------------------------------------    document.onkeydown = function (event) {        var e = event || window.event || arguments.callee.caller.arguments[0];        if (e && e.keyCode == 27) { // 按 Esc             //要做的事情        }        if (e && e.keyCode == 13) { // enter 键            //要做的事情            DataTablesObj.reloadList(DataTablesObj.table_local);        }    };    ////对行单击添加监听事件    //$('#table_local tbody').on('click', 'tr', function () {    //    var tr = $(this).closest('tr');    //    var checkbox = tr.find('td:first-child input:checkbox')[0];    //    checkbox.checked = !checkbox.checked;    //});    //获Ggridview中所有的复选框 sName: Gridview 的ID      this.getCheckbox = function (sName) {        var aryCheckbox = new Array();        var tb = document.getElementById(sName);        if (tb == null)            return;        var objs = tb.getElementsByClassName("cbx");        for (var i = 0; i < objs.length; i++) {            if (objs[i].type == 'checkbox')                aryCheckbox.push(objs[i]);        }        return aryCheckbox;    };    //监听每一行的复选框,控制全选、反选按钮      this.controlSelectAll = function (i) {        var tblName, cbkAll; //Gridview ID ,全选框ID          var tblName = DataTablesObj.table_local;        var cbkAll = DataTablesObj.chkAllColl;        var id = "#cbx" + i;        //点击复选框选中行        //if ($(id)[0].checked == true) {        //    $(id).parent().parent().addClass('selected');        //    $(id).parent().parent().siblings().removeClass('selected');        //} else {        //    $(id).parent().parent().siblings().removeClass('selected');        //    $(id).parent().parent().removeClass('selected');        //}        var chks = DataTablesObj.getCheckbox(tblName);        var count = 0;        for (var i = 0; i < chks.length; i++) {            if (chks[i].checked == true) {                count++;            }        }        if (count < chks.length) {            document.getElementById(cbkAll).checked = false;        }        else {            document.getElementById(cbkAll).checked = true;        }    };    //全选反选    this.selectAll = function () {        var isChecked = $("#" + DataTablesObj.chkAllColl)[0].checked;        $("input[name='chkItem']").prop("checked", isChecked);    };    //查询刷新([datatable ID])    this.reloadList = function (id) {        var tableId = id == undefined ? DataTablesObj.table_local : id;        var tables = $('#' + tableId).dataTable().api();//获取DataTables的Api,详见 http://www.datatables.net/reference/api/        tables.ajax.reload();    };    //****************************************************************************************************************    //新增记录(异步请求界面url,[modal ID],[回调函数])    this.doCreateModal = function (url, modalId, func) {        var modalId = modalId == undefined ? DataTablesObj.modalId : modalId;        $('#' + modalId).modal({ show: true, backdrop: 'static', remote: url });        if (func != undefined) {            func();        }    };    //详细记录(主键ID,[modal ID],[异步请求界面url])    this.doReadModal = function (id, modalId, readUrl) {        var modalId = modalId == undefined ? DataTablesObj.modalId : modalId;        var url = readUrl == undefined ? DataTablesObj.readUrl : readUrl;        $('#' + DataTablesObj.modalId).modal({ show: true, backdrop: 'static', remote:url  + "?id=" + id });    }    //编辑记录(主键ID,[异步请求界面url],[modal ID])    this.doUpdateModal = function (id, modalId,updateUrl) {        var modalId = modalId == undefined ? DataTablesObj.modalId : modalId;        var url = updateUrl == undefined ? DataTablesObj.updateUrl : updateUrl;        $('#' + modalId).modal({ show: true, backdrop: 'static', remote: url + "?id=" + id });    };    //删除选中记录(批量删除url地址)    this.doDeleteList = function (url) {        var table = $('#' + DataTablesObj.table_local).dataTable();        var nTrs = table.fnGetNodes();//fnGetNodes获取表格所有行,nTrs[i]表示第i行tr对象        var row;        var strdid = '';        var selectCounts = 0;        for (var i = 0; i < nTrs.length; i++) {            if ($(nTrs[i])[0].cells[0].children[0].checked) {                row = table.fnGetData(nTrs[i]);//fnGetData获取一行的数据                        selectCounts++;                strdid += "" + row.ID + ",";            }        }        strdid = strdid.substring(0, strdid.length - 1);        var ids = strdid.split(",");        if (selectCounts < 1) {            toastr.warning("请先选择数据行!");            return false;        }        $.ajax({            type: 'POST',            url: url,            data: { 'ids': ids },            dataType: 'json',            success: function (result) {                toastr.success(result.message);                DataTablesObj.reloadList(DataTablesObj.table_local);            }        });    }    //删除单条记录(控件id,主键id)    this.doDelete = function (btn, id) {        $(btn).on('confirmed.bs.confirmation', function () {            $.post(DataTablesObj.deleteUrl, { Id: id }, function (result) {                toastr.success(result.message);                DataTablesObj.reloadList(DataTablesObj.table_local);            });        });        $(btn).confirmation('show');    };    this.options = {        bProcessing: true,        //"scrollY": table_h1,// 340,//         "scrollX": false,        //"scrollCollapse": "true",        //dom:"",//'ftr<"bottom"lip>',//<"clear">        "bServerSide": false, //指定从服务器端获取数据        sServerMethod: "POST",        sAjaxSource: "",        autoWidth: false,        fnServerParams: null,        columns: null,        paging: true,//分页        ordering: true,//是否启用排序        searching: false,//搜索        language: {            "sProcessing": "处理中...",            lengthMenu: lengthMenuStr,//左上角的分页大小显示。            search: '<span class="label label-success">搜索:</span>',//右上角的搜索文本,可以写html标签            //paginate: {//分页的样式内容。            //    previous: "上一页",            //    next: "下一页",            //    first: "",            //    last: ""            //},            "paginate": {                "previous": "Prev",                "next": "Next",                "last": "Last",                "first": "First"            },            zeroRecords: "",//table tbody内容为空时,tbody的内容。--暂无记录            //下面三者构成了总体的左下角的内容。            info: infoStr,//左下角的信息显示,大写的词为关键字。初始_MAX_ 条            infoEmpty: "0条记录",//筛选为空时左下角的显示。            infoFiltered: ""//筛选之后的左下角筛选提示,        },        pagingType: "bootstrap_full_number"//分页样式的类型 "full_numbers"//    };    //监听批量删除按钮事件(控件ID,url删除地址)    this.listenerDeleteEvent = function (ctrlId, url) {        $('#' + ctrlId).on('confirmed.bs.confirmation', function () {            DataTablesObj.doDeleteList(url);        }).on("click", function () {            $('#' + ctrlId).confirmation('show');        });    }    //控件初始化(配置项,[table容器ID],[删除对象])    this.init = function (options, tableId,obj) {        var tableId = tableId == undefined ? DataTablesObj.table_local : tableId;        var opts = options == undefined ? DataTablesObj.options : options;        $('#' + tableId).dataTable(opts);        if (obj == undefined) {            DataTablesObj.listenerDeleteEvent(DataTablesObj.batchDeleteBtn, DataTablesObj.batchDeleteUrl);        } else {            obj.listenerDeleteEvent(obj.batchDeleteBtn, obj.batchDeleteUrl);        }              delete options.aoColumns;//同一个页面多处使用时,要先删除此对象,具体原因不明    };    //批量初始化(options配置数组,tables id数组)-不添加监听事件    this.initList = function (opts, ids) {        if (opts!=undefined &&opts!=null &&opts.length>0&& ids != undefined&& ids!=null && ids.length > 0) {            var length = ids.length;            for (var i = 0; i < length; i++) {                $('#' + ids[i]).dataTable(opts[i]);            }        }    }    return this;}).call({});

View Code

我们进行二次封装的UI组件对象,他们的options属性,是复杂属性,也就是类似与引用类型,要使用深拷贝然后再去修改,否则修改的是引用而不是副本。我这里没有去使用clone,而是直接在options对象上进行赋值了,那是因为我赋值的那些属性每个引用页面都会去再赋值一遍,而js是运行在客户端的,也就是说每个客户的电脑上面都会有一份完整的js文件副本,这和运行在服务器端的C#语言是不一样的。其实使用  var options = clone(DataTablesObj.options);再去给options赋值是标准做法,但是会失去一部分性能。我的js水平太菜,所以封装得不是特别好,但是至少页面干净、方便维护。

js方法上面的注释,我也是按照自定义的风格进行注释,参数用()括起来,可选参数就用[],这些东西都可以当成约定或者规范,目的就是为了让所有的开发人员写的代码像一个人写的。本来我想把方法的参数都封装成一个对象的,只是之前觉得参数个数少,就没有那样去封装了,参数用对象的好处就是可以无序,而且更易扩展,所有这些进行二次封装的UI组件,文件命名我都加了前缀base-,其实里面的对象命名我是加的后缀Obj,你也可以根据自己的爱好设定,比如说公司名称简写作为前缀,但是一定确定了,就要团队成员遵守约定。

日期组件同样进行封装base-Datetimepicker.js,这里的打印我暂时就没有去进行封装了。在js中通常使用原型链的方式来实现继承,我这里没有使用,原因2点,一是自身对这块掌握得不好,二是公司都是后端开发人员,就按照这种最简单的方式进行封装。

.Net MVC&&datatables.js&&bootstrap做一个界面的CRUD有多简单插图20

//ajax加载调试用//# sourceURL=base-Datetimepicker.js//Datetimepicker日期组件扩展对象--created by zouqj 2017-7-13var DatetimepickerObj = (function () {    this.options = {        language: 'zh-CN',//显示中文        format: 'yyyy-mm-dd',//显示格式        minView: "month",//设置只显示到月份        initialDate: new Date(),//初始化当前日期        autoclose: true,//选中自动关闭        todayBtn: true//显示今日按钮    };    this.init= function (ctrlId) {        $("#" + ctrlId).datetimepicker(DatetimepickerObj.options);    }    //(开始日期控件,结束日期控件)    this.initStartEnd = function (startCtrl, endCtrl) {        var startCtrl = $("#" + startCtrl);        var endCtrl = $("#" + endCtrl);        startCtrl.datetimepicker({            format: 'yyyy-mm-dd',            minView: 'month',            language: 'zh-CN',            autoclose: true,            startDate: new Date()        }).on("click", function () {            startCtrl.datetimepicker("setEndDate", endCtrl.val())        });        endCtrl.datetimepicker({            format: 'yyyy-mm-dd',            minView: 'month',            language: 'zh-CN',            autoclose: true,            startDate: new Date()        }).on("click", function () {            endCtrl.datetimepicker("setStartDate", startCtrl.val())        });    }    return this;}).call({});   

View Code

那么其实我们界面上真正自己要写的就3个View,3个js,一个控制器提供对应的方法调用,其它的不管是C#还是js都去进行封装,css样式通通按照规范写成皮肤文件。项目中可以有一个皮肤css,一个布局css,一个自定义css。

所以说.net做东西就是这么简单,看似复杂的功能界面,一下子就搞定了,只要把东西都封装好了,做一个这样的功能最多大半天就搞定,仔细看下js总共就写几十行左右,还算上了复制粘贴修改下的,哪里需要去经常加班?加班应该是php程序员和java程序员的专利,做不加班的.net程序员~

文章转载于:https://www.cnblogs.com/jiekzou/p/7430043.html

原著是一个有趣的人,若有侵权,请通知删除

未经允许不得转载:起风网 » .Net MVC&&datatables.js&&bootstrap做一个界面的CRUD有多简单
分享到: 生成海报

评论 抢沙发

评论前必须登录!

立即登录