MVC无限级分类02,增删改查
继上一篇"MVC无限级分类01,分层架构,引入缓存,完成领域模型与视图模型的映射",本篇开始MVC无限级分类的增删改查部分,源码在github。
显示和查询
使用datagrid显示数据,向控制器发出请求的时候,datagrid默认带上了当前页page和页容量rows这2个参数。如果项目中的其它界面也用datagrid展示数据,我们可以把page和rows封装成一个基类:
namespace Car.Test.Portal.Models.QueryParams
{
public class QueryParamsBase
{
public int PageSize { get; set; }
public int PageIndex { get; set; }
}
}
而当输入搜索条件时,datagrid的queryParams属性能接收json格式的搜索条件,这时候,datagrid向控制器发出请求,不仅仅有page和rows这2个参数,还包括了搜索条件中的键值对。可以把与每个模型对应的搜索条件封装成一个继承QueryParamsBase的类:
using System;
namespace Car.Test.Portal.Models.QueryParams
{
public class CarCategoryQueryParams : QueryParamsBase
{
public string Name { get; set; }
public DateTime? JoinStartTime { get; set; }
public DateTime? JoinEndTime { get; set; }
}
}
与搜索和显示相关的html部分为:
<!--查询开始-->
<div id="query" class="query" >
<span>类名:</span>
<input id="txtName" style="line-height:22px;border:1px solid #ccc" maxlength="10">
<span>提交时间从:</span>
<input type="text" name="startSubTime" id="startSubTime" style="line-height:22px;border:1px solid #ccc" />
<span>至:</span>
<input type="text" name="endSubTime" id="endSubTime" style="line-height:22px;border:1px solid #ccc" />
<a href="#" class="easyui-linkbutton" plain="false" id="btnSearch">搜索</a>
</div>
<!--查询结束-->
<!--表格开始-->
<div class="ctable" id="ctable">
<table id="tt"></table>
</div>
<!--表格结束-->
js部分需要完成的工作包括:
●限制搜索条件中类名的长度
●搜索条件中的时间用jqueryui的datapicker来实现,当在起始时间选择了一个日期,结束日期的选择范围相应变成该天以后
●起始日期和结束日期只能通过datapicker设置,所以要把2个文本框设置为只读
●点击搜索按钮,把搜索添加封装成json对象传递个datagrid的queryParams属性
<script type="text/javascript">
$(function() {
//限制类名的长度
limitInputLength($('#txtActionName'));
//限制时间搜索为只读
$('#startSubTime').attr('readonly', true);
$('#endSubTime').attr('readonly', true);
//起始和结束日期
fromDateToDate();
//显示列表
initData();
//搜索
$('.query').on("click", "#btnSearch", function () {
initData(initQuery());
});
});
//限制文本框长度
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 fromDateToDate() {
$('#startSubTime').datepicker({
dateFormat: "yy-mm-dd",
changeMonth: true,
changeYear: true,
numberOfMonths: 2,
onClose: function (selectedDate) {
$("#endSubTime").datepicker("option", "minDate", selectedDate);
}
});
$('#endSubTime').datepicker({
dateFormat: "yy-mm-dd",
changeMonth: true,
changeYear: true,
numberOfMonths: 2,
onClose: function (selectedDate) {
$("#startSubTime").datepicker("option", "maxDate", selectedDate);
}
});
}
//获取查询表单的值组成json
function initQuery() {
var queryParams = {
txtName: $('#txtName').val(),
startSubTime: $('#startSubTime').val()
};
return queryParams;
}
//显示分类
function initData(params) {
$('#tt').datagrid({
url: '@Url.Action("GetCarCategoryJson","Home")',
title: '分类列表',
//width: 800,
height: 390,
fitColumns: true,
nowrap: true,
showFooter: true,
idField: 'ID',
loadMsg: '正在加载信息...',
pagination: true,
singleSelect: false,
queryParams: params,
pageSize: 10,
pageNumber: 1,
pageList: [10, 20, 30],
//toolbar: '#query',
columns: [
[
{ field: 'ck', checkbox: true, align: 'center', width: 30 },
{ field: 'ID', title: '编号' },
{ field: 'Name', title: '类名' },
{ field: 'PreLetter', title: '前缀字母' },
{ field: 'ParentID', title: '父级ID' },
{ field: 'Level', title: '层级' },
{ field: 'IsLeaf', title: '是否叶节点' },
{
field: 'DelFlag',
title: '删除状态',
formatter: function (value, row, index) {
if (value == "0") {
return '使用中';
} else if (value == "1") {
return '逻辑删除';
} else {
return '物理删除';
}
}
},
{
field: 'SubTime',
title: '修改时间',
formatter: function (value, row, index) {
return eval("new " + value.substr(1, value.length - 2)).toLocaleDateString();
}
}
]
],
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("DeleteCarCategories", "Home")", { ids: strIds }, function (data) {
if (data.msg == "no") {
$.messager.alert("提示", "删除失败!");
$('#tt').datagrid("clearSelections");
return;
} else if (data.msg) {
$.messager.alert("提示", "删除成功");
initData(initQuery());
$('#tt').datagrid("clearSelections");
}
});
}
});
}
}
],
OnBeforeLoad: function (param) {
return true;
}
});
}
</script>
控制器部分,把接收到的有关分页或查询的参数封装成一个类作为参数交给服务层的一个方法,这里为了方便,把本该在服务层的方法直接写在了控制器内。最后把得到的集合封装成json对象,并满足datagrid所期望的格式传递到前台视图。
private string txtName = string.Empty;
private DateTime? startSubTime = null;
private DateTime? endSubTime = null;
public ICarCategoryRepository CarCategoryRepository { get; set; }
public HomeController(ICarCategoryRepository carCategoryRepository)
{
this.CarCategoryRepository = carCategoryRepository;
}
public HomeController():this(new CarCategoryRepository()){}
#region 显示分类
public ActionResult Index()
{
return View();
}
//显示所有分类并考虑查询和分页
public ActionResult GetCarCategoryJson()
{
//获取datagrid传来的2个参数
int pageIndex = int.Parse(Request["page"]);
int pageSize = int.Parse(Request["rows"]);
//获取搜索参数
if (!string.IsNullOrEmpty(Request["txtName"]))
{
txtName = Request["txtName"];
}
if (!string.IsNullOrEmpty(Request["startSubTime"]))
{
startSubTime = DateTime.Parse(Request["startSubTime"]);
}
if (!string.IsNullOrEmpty(Request["endSubTime"]))
{
endSubTime = DateTime.Parse(Request["endSubTime"]);
}
//初始化查询实例
var temp = new CarCategoryQueryParams
{
PageIndex = pageIndex,
PageSize = pageSize,
Name = txtName,
JoinEndTime = endSubTime,
JoinStartTime = startSubTime
};
//获取所有满足条件的数据,并获得总记录数
int totalNum = 0;
var allCarCategory = LoadPageCarCategoryData(temp, out totalNum);
//投影出需要传递到前台的数据
var result = from c in allCarCategory
select new
{
c.ID,
c.Name,
c.IsLeaf,
c.Level,
c.ParentID,
c.PreLetter,
c.Status,
c.DelFlag,
c.SubTime
};
//构建datagrid所需要的json格式
var jsonResult = new { total = totalNum, rows = result };
return Json(jsonResult, JsonRequestBehavior.AllowGet);
}
//根据查询条件获得分页数据
private IEnumerable<CarCategory> LoadPageCarCategoryData(CarCategoryQueryParams param, out int total)
{
var allCarCategories =
CarCategoryRepository.LoadEntities(
c => c.Status == (short) StatusEnum.Enable && c.DelFlag == (short) DelFlagEnum.Normal);
if (!string.IsNullOrEmpty(param.Name))
{
allCarCategories = allCarCategories.Where(c => c.Name.Contains(param.Name));
}
if (!string.IsNullOrEmpty(param.JoinStartTime.ToString()) &&
!string.IsNullOrEmpty(param.JoinEndTime.ToString()))
{
allCarCategories =
allCarCategories.Where(c => c.SubTime >= param.JoinStartTime && c.SubTime <= param.JoinEndTime);
}
total = allCarCategories.Count();
IEnumerable<CarCategory> result = allCarCategories
.OrderByDescending(c => c.ID)
.Skip(param.PageSize*(param.PageIndex - 1))
.Take(param.PageSize);
return result;
}
#endregion
添加
在主视图,即datagrid所在页面视图,放置显示添加弹出窗口的div,里面有一个iframe。
在视图刚加载完的时候,先把添加div隐藏掉。
$(function() {
//隐藏元素
initialHide();
});
//隐藏元素
function initialHide() {
$('.addContent').css("display", "none");
$('.editContent').css("display", "none");
}
当点击datagrid添加按钮,向控制器方法/Home/AddCarCategory发送一个ajax的get请求,如果请求到数据,ifram指向到新的视图,显示添加div,并以模态窗口的形式弹出。
//添加对话框
function showAdd() {
var url = "/Home/AddCarCategory";
$.ajax({
cache: false,
url: url,
contentType: 'application/html;charset=utf-8',
type: "GET",
success: function (result) {
if (result) {
$("#frameAdd").attr("src", url);
$("#addContent").css("display", "block");
$('#addContent').dialog({
toolbar: [],
maximizable: false,
draggable: true,
resizable: false,
closable: false,
modal: true,
cache: false,
//height: '100%',
width: 560,
height: 480,
top:50,
title: "添加类别",
onOpen: function () {
},
buttons: []
});
}
},
error: function (xhr, status) {
alert("加载内容失败" + status);
}
});
}
当iframe指向的视图执行完毕后还是要返回到datagrid所在视图界面的,可以把iframe指向的视图页看作子窗口,从datagrid主视图跳出的模态窗口看作是父窗口,让子窗口执行完毕后,调用父窗口的方法。父窗口被子窗口调用的添加成功或取消添加的方法为:
//添加成功后
function refreshAfterAdd() {
initData();
$('#tt').datagrid("clearSelections");
$('.addContent').dialog("close");
}
//取消添加
function cancelAdd() {
$('.addContent').dialog("close");
}
添加div中iframe指向的视图页面,在逻辑上可以分成2部分,一部分是以视图模型以及模型验证有关的,我们把它方在一个强类型部分视图中,再通过jquery异步加载它。另外一部分就是添加按钮和取消,点击它们,调用父窗口的方法。
控制器中的 AddCarCategory()用来显示添加div中iframe指向的视图页面,AddCarCategory(CarCategoryVm carCategoryVm)用来处理添加,AddModel()用来显示强类型部分视图,在AddCarCategory.cshtml视图中通过jquery异步加载。控制器部分为:
#region 添加分类
public ActionResult AddCarCategory()
{
//return PartialView(new CarCategoryVm());
return View();
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult AddCarCategory(CarCategoryVm carCategoryVm)
{
if (ModelState.IsValid)
{
carCategoryVm.Status = (short)StatusEnum.Enable;
carCategoryVm.DelFlag = (short)DelFlagEnum.Normal;
carCategoryVm.SubTime = DateTime.Now;
CarCategory carCategory = AutoMapper.Mapper.DynamicMap<CarCategoryVm, CarCategory>(carCategoryVm);
CarCategoryRepository.AddEntity(carCategory);
CarCategoryRepository.SaveChanges();
return Json(new {msg = true});
}
else
{
//return PartialView(carCategoryVm);
return PartialView("AddModel", carCategoryVm);
}
}
//[ChildActionOnly]
public ActionResult AddModel()
{
return PartialView(new CarCategoryVm());
}
#endregion
#region 显示所有分类的树
public string LoadAllCategories()
{
var temp =
CarCategoryRepository.LoadEntities(
c => c.DelFlag == (short) DelFlagEnum.Normal && c.Status == (short) StatusEnum.Enable);
var result = from c in temp
select new {c.ID, c.Name, c.ParentID};
//return Json(result, JsonRequestBehavior.AllowGet);
return JsonSerializeHelper.SerializeToJson(result);
}
#endregion
AddModel.cshtml强类型部分视图,我们希望点击id="getCategory"的input的时候,显示zTree树,而点击zTree节点的时候,id="getCategory"的input显示节点名称,把真正的ID保存到id="categoryId" name="ParentID"的隐藏域中。
@model Car.Test.Portal.Models.CarCategoryVm
@using (Html.BeginForm("AddCarCategory", "Home", FormMethod.Post, new { id = "addForm" }))
{
@Html.AntiForgeryToken()
<ul>
<li>
<span class="fdwith">
@Html.LabelFor(c => c.Name)
</span>
@Html.EditorFor(c => c.Name)
@Html.ValidationMessageFor(c => c.Name)
</li>
<li>
<span class="fdwith">
@Html.LabelFor(c => c.PreLetter)
</span>
@Html.EditorFor(c => c.PreLetter)
@Html.ValidationMessageFor(c => c.PreLetter)
</li>
<li>
<span class="fdwith">
@Html.LabelFor(c => c.ParentID)
</span>
<input id="getCategory" type="text" value="==请选择分类==" />
@Html.ValidationMessageFor(c => c.ParentID)
<input type="hidden" id="categoryId" name="ParentID"/>
</li>
<li>
<span class="fdwith">
@Html.LabelFor(c => c.Level)
</span>
@Html.EditorFor(c => c.Level)
@Html.ValidationMessageFor(c => c.Level)
</li>
<li>
<span class="fdwith">
@Html.LabelFor(c => c.IsLeaf)
</span>
@Html.DropDownListFor(c => c.IsLeaf,new[]
{
new SelectListItem(){Text = "是",Value = bool.TrueString},
new SelectListItem(){Text = "否",Value = bool.FalseString}
},"==选择是否为叶节点==",new {id="ld"})
@Html.ValidationMessageFor(c => c.IsLeaf)
</li>
</ul>
}
AddCarCategory.cshtml视图中,js和css部分需注意:
● zTree所需要的1个css文件和2个js文件一个都不能少
● zTree需要在ul的class一定是ztree,即<ul id="tree" class="ztree"...,否则会影响树的显示,本人就吃过这个苦头,擅自把ztree改成tree,为这个问题纠结了一天!
● 关于jquery验证的jquery.validate.js文件和jquery.validate.unobtrusive.js顺序千万不能颠倒。
● zTree的属性较多,setting中的属性,本人也调了很多次,现在看到的还不错。
● 要把zTree所在的div设置成绝对定位,因为希望在显示的时候通过js控制,让它跑到对应的文本框正下方。
AddCarCategory.cshtml视图主要工作包括:
● 页面加载完毕,向控制器发送异步请求,把zTree树先加载上,由于隐藏了zTree所在的div,这时候还看不到
● 页面加载完毕,项控制器发送异步请求,把强类型部分视图加载到本页。由于是动态加载的内容,所以需要$.validator.unobtrusive.parse("form")此语句,让动态部分视图内容恢复客户端验证功能。
● 点击取消按钮,调用父窗口的取消添加方法
● 点击添加按钮,调用父窗口添加成功方法
● 由于生成的是动态内容,根据"冒泡原理",需要不添加按钮up等的点击事件注册到它的父级元素,类似这样:$('#operate').on("click", "#up", function() {}
● 点击部分视图的id="getCategory"的input的时候,显示zTree树,而点击zTree节点的时候,id="getCategory"的input显示节点名称,把真正的ID保存到id="categoryId" name="ParentID"的隐藏域中。
展开@{
Layout = null;
} <!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<title>添加类别</title>
<link href="~/Content/zTreeStyle/zTreeStyle.css" rel="stylesheet" />
<link href="~/Content/themes/gray/easyui.css" rel="stylesheet" />
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery-migrate-1.2.1.min.js"></script>
<script src="~/Scripts/zTree_v3/jquery.ztree.core-3.5.js"></script>
<script src="~/Scripts/zTree_v3/jquery.ztree.excheck-3.5.js"></script>
<script src="~/Scripts/jquery.easyui.min.js"></script>
<script src="~/Scripts/easyui-lang-zh_CN.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script> <style type="text/css">
.field-validation-error {
color: red;
} .fdwith {
display: inline-block;
width: 150px;
} ul {
list-style-type: none;
}
</style>
<script type="text/javascript">
var setting = {
check: {
enable: true,
chkStyle: "radio",
radioType: "all"
//chkboxType: { "Y": "ps", "N": "ps" }
},
view: {
dblClickExpand: false,
showLine: true,
expandSpeed: "fast",
showIcon: false
},
data: {
simpleData: {
enable: true,
idKey: "ID",
pIdKey: "ParentID",
rootPid: 0
},
key: {
name: "Name",
pId: "ParentID",
id: "ID"
}
},
callback: { //回调函数
//dblclick: zTreeOnDblclick
onClick: onClick,
onCheck: onCheck
}
}; var zTree; //点击节点改变节点的选中状态
function onClick(e, treeId, treeNode) {
zTree.checkNode(treeNode, !treeNode.checked, null, true);
return false;
} //把选中节点的name属性值显示到input上
function onCheck(e, treeId, treeNode) {
var nodes = zTree.getCheckedNodes(true),
v = "",vId="";
for (var i = 0, l = nodes.length; i < l; i++) {
v += nodes[i].Name + ",";
vId += nodes[i].ID + ",";
}
if (v.length > 0) v = v.substring(0, v.length - 1);
if (vId.length > 0) vId = vId.substring(0, vId.length - 1);
$("#getCategory").attr("value", v);
$("#categoryId").attr("value", vId);
} //显示树
function showMenu() {
var cObj = $("#getCategory");
var cOffset = $("#getCategory").offset(); $("#CategoryDiv").css({ left: cOffset.left + "px", top: cOffset.top + cObj.outerHeight() + "px" }).slideDown("fast"); //给树定位
$("body").bind("mousedown", onBodyDown);//绑定鼠标单击事件
} //隐藏树并解除绑定
function hideMenu() {
$("#CategoryDiv").fadeOut("fast");
$("body").unbind("mousedown", onBodyDown);
} //鼠标单击空白处事件
function onBodyDown(event) {
if (!(event.target.id == "getCategory" || event.target.id == "CategoryDiv" || $(event.target).parents("#CategoryDiv").length > 0)) {
hideMenu();
if ($('#getCategory').val().length === 0) {
$('#getCategory').val("==请选择分类==");
} }
} $(function () { //点击取消执行父窗口的方法
$('#cancel').on("click", function () {
self.parent.cancelAdd();
}); //隐藏树
$("#CategoryDiv").css("display", "none"); //预先加载树
$.ajax({
cache: false,
url: '@Url.Action("LoadAllCategories", "Home")',
type: "GET",
dataType: "text",
success: function (result) {
$.fn.zTree.init($("#tree"), setting, eval('(' + result + ')'));
zTree = $.fn.zTree.getZTreeObj("tree");
zTree.expandAll(true);
},
error: function (xhr, status) {
alert("加载类别失败" + status);
}
}); //加载部分视图内容
$.ajax({
cache: false,
url: '@Url.Action("AddModel", "Home")',
contentType: 'application/html;charset=utf-8',
type: "GET",
success: function (result) {
$('#content').html(result);
$('#ld').val(""); //让dropdownlist选择默认项
//限制请选择分类为只读
$('#getCategory').attr('readonly', true);
$.validator.unobtrusive.parse("form");
},
error: function (xhr, status) {
alert("加载内容失败" + status);
}
}); //点击修改发送异步请求到控制器
$('#operate').on("click", "#up", function() {
$.ajax({
cache: false,
url: '@Url.Action("AddCarCategory", "Home")',
type: "POST",
data: $('#addForm').serialize(),
success: function (data) {
if (data.msg) {
self.parent.refreshAfterAdd();
} else {
$('#content').html(data);
$('#ld').val(""); //让dropdownlist选择默认项
//限制请选择分类为只读
$('#getCategory').attr('readonly', true);
$.validator.unobtrusive.parse("form");
}
},
error: function (jqXhr, textStatus, errorThrown) {
alert("出错了 '" + jqXhr.status + "' (状态: '" + textStatus + "', 错误为: '" + errorThrown + "')");
}
});
}); //点击选择分类
$('#content').on("click", "#getCategory", function() {
$("#CategoryDiv").css("display", "block");
showMenu();
}); });
</script>
</head>
<body style="height:550px;">
<div id="wrapper">
<div id="content">
</div>
<div id="operate" style="text-align: center;">
<input type="button" id="up" value="添加" /> <input type="button" id="cancel" value="取消/关闭窗口"/>
</div> <div id="CategoryDiv" style="position: absolute;background-color: #e3e3e3;text-align: center;">
<input type="button" value="展开" onclick="zTree.expandAll(true);"/>
<input type="button" value="折叠" onclick="zTree.expandAll(false);"/>
<hr/> <ul id="tree" class="ztree" style="margin-top:0; width:180px; height: 250px; overflow:auto;margin: 0 auto 0 auto;"></ul>
</div>
</div>
</body>
</html>
修改
在主视图,即datagrid所在页面视图,放置显示修改弹出窗口的div,里面有一个iframe。
在视图刚加载完的时候,先把修改div隐藏掉。
$(function() {
//隐藏元素
initialHide();
});
//隐藏元素
function initialHide() {
$('.addContent').css("display", "none");
$('.editContent').css("display", "none");
}
当点击datagrid修改按钮,向控制器方法"/Home/EditCarCategory?发送一个ajax的get请求,如果请求到数据,ifram指向到新的视图,显示修改div,并以模态窗口的形式弹出。
//修改对话框
function showEdit(carCatId) {
var url = "/Home/EditCarCategory?id=" + carCatId;
$.ajax({
cache: false,
url: url,
contentType: 'application/html;charset=utf-8',
type: "GET",
success: function (result) {
if (result) {
$("#frameEdit").attr("src", url);
$("#editContent").css("display", "block");
$('#editContent').dialog({
toolbar: [],
maximizable: false,
draggable: true,
resizable: false,
closable: false,
modal: true,
cache: false,
//height: '100%',
width: 560,
title: "修改类别",
onOpen: function () {
},
buttons: []
});
}
},
error: function (xhr, status) {
alert("加载内容失败" + status);
}
});
}
当iframe指向的视图执行完毕后还是要返回到datagrid所在视图界面的,可以把iframe指向的视图页看作子窗口,从datagrid主视图跳出的模态窗口看作是父窗口,让子窗口执行完毕后,调用父窗口的方法。父窗口被子窗口调用的修改成功或取消修改的方法为:
//修改成功后
function refreshAfterEdit() {
initData();
$('#tt').datagrid("clearSelections");
$('.editContent').dialog("close");
}
//取消修改
function candelEdit() {
$('#tt').datagrid("clearSelections");
$('.editContent').dialog("close");
}
修改的控制器部分把从前台视图拿到的参数id存放到ViewData中,传到到编辑子窗口,异步加载强类型部分视图的时候再用到这个id。
#region 编辑分类
public ActionResult EditCarCategory(int id)
{
ViewData["id"] = id;
return View();
}
public ActionResult EditModel(int id)
{
var carCategoryDb = CarCategoryRepository.LoadEntities(c => c.ID == id).FirstOrDefault();
CarCategoryVm carCategoryVm = AutoMapper.Mapper.DynamicMap<CarCategory, CarCategoryVm>(carCategoryDb);
return PartialView(carCategoryVm);
}
[HttpPost]
[ValidateAntiForgeryToken]
public ActionResult EditCarCategory(CarCategoryVm carCategoryVm)
{
if (ModelState.IsValid)
{
carCategoryVm.Status = (short)StatusEnum.Enable;
carCategoryVm.DelFlag = (short)DelFlagEnum.Normal;
carCategoryVm.SubTime = DateTime.Now;
CarCategory carCategory = AutoMapper.Mapper.DynamicMap<CarCategoryVm, CarCategory>(carCategoryVm);
CarCategoryRepository.UpdateEntity(carCategory);
CarCategoryRepository.SaveChanges();
return Json(new {msg = true});
}
else
{
return PartialView("EditModel", carCategoryVm);
}
}
#endregion
编辑强类型部分视图中, @Html.HiddenFor(c => c.ParentID,new {id = "categoryId"})隐藏域用来显示由控制器方法传递的视图模型中的ParentID值。并且,会根据这个ParentID把对应的节点名称显示到<input id="getCategory" type="text" value="==请选择分类==" />上。
@model Car.Test.Portal.Models.CarCategoryVm
@using (Html.BeginForm("EditCarCategory", "Home", FormMethod.Post, new { id = "editForm" }))
{
@Html.AntiForgeryToken()
@Html.HiddenFor(c => c.ID)
<ul>
<li>
<span class="fdwith">
@Html.LabelFor(c => c.Name)
</span>
@Html.EditorFor(c => c.Name)
@Html.ValidationMessageFor(c => c.Name)
</li>
<li>
<span class="fdwith">
@Html.LabelFor(c => c.PreLetter)
</span>
@Html.EditorFor(c => c.PreLetter)
@Html.ValidationMessageFor(c => c.PreLetter)
</li>
<li>
<span class="fdwith">
@Html.LabelFor(c => c.ParentID)
</span>
<input id="getCategory" type="text" value="==请选择分类==" />
@Html.ValidationMessageFor(c => c.ParentID)
@Html.HiddenFor(c => c.ParentID,new {id = "categoryId"})
</li>
<li>
<span class="fdwith">
@Html.LabelFor(c => c.Level)
</span>
@Html.EditorFor(c => c.Level)
@Html.ValidationMessageFor(c => c.Level)
</li>
<li>
<span class="fdwith">
@Html.LabelFor(c => c.IsLeaf)
</span>
@Html.DropDownListFor(c => c.IsLeaf,new[]
{
new SelectListItem(){Text = "是",Value = bool.TrueString},
new SelectListItem(){Text = "否",Value = bool.FalseString}
},"==选择是否为叶节点==",new {id="ld"})
@Html.ValidationMessageFor(c => c.IsLeaf)
</li>
</ul>
}
编辑视图的原理与添加视图类似,只不过要根据从控制器方法传递过来的视图模型中的ParentID所对应的节点名称显示到强类型部分视图的<input id="getCategory" type="text" value="==请选择分类==" />上。
展开@{
Layout = null;
} <!DOCTYPE html> <html>
<head>
<meta name="viewport" content="width=device-width" />
<title>编辑类别</title>
<link href="~/Content/zTreeStyle/zTreeStyle.css" rel="stylesheet" />
<link href="~/Content/themes/gray/easyui.css" rel="stylesheet" />
<script src="~/Scripts/jquery-1.10.2.js"></script>
<script src="~/Scripts/jquery-migrate-1.2.1.min.js"></script>
<script src="~/Scripts/zTree_v3/jquery.ztree.core-3.5.js"></script>
<script src="~/Scripts/zTree_v3/jquery.ztree.excheck-3.5.js"></script>
<script src="~/Scripts/jquery.easyui.min.js"></script>
<script src="~/Scripts/easyui-lang-zh_CN.js"></script>
<script src="~/Scripts/jquery.validate.js"></script>
<script src="~/Scripts/jquery.validate.unobtrusive.js"></script>
<style type="text/css">
.field-validation-error {
color: red;
} .fdwith {
display: inline-block;
width: 150px;
} ul {
list-style-type: none;
}
</style>
<script type="text/javascript">
var setting = {
check: {
enable: true,
chkStyle: "radio",
radioType: "all"
//chkboxType: { "Y": "ps", "N": "ps" }
},
view: {
dblClickExpand: false,
showLine: true,
expandSpeed: "fast",
showIcon: false
},
data: {
simpleData: {
enable: true,
idKey: "ID",
pIdKey: "ParentID",
rootPid: 0
},
key: {
name: "Name",
pId: "ParentID",
id: "ID"
}
},
callback: { //回调函数
//dblclick: zTreeOnDblclick
onClick: onClick,
onCheck: onCheck
}
}; var zTree; //点击节点改变节点的选中状态
function onClick(e, treeId, treeNode) {
zTree.checkNode(treeNode, !treeNode.checked, null, true);
return false;
} //把选中节点的name属性值显示到input上
function onCheck(e, treeId, treeNode) {
var nodes = zTree.getCheckedNodes(true),
v = "", vId = "";
for (var i = 0, l = nodes.length; i < l; i++) {
v += nodes[i].Name + ",";
vId += nodes[i].ID + ",";
}
if (v.length > 0) v = v.substring(0, v.length - 1);
if (vId.length > 0) vId = vId.substring(0, vId.length - 1);
$("#getCategory").attr("value", v);
$("#categoryId").attr("value", vId);
} //显示树
function showMenu() {
var cObj = $("#getCategory");
var cOffset = $("#getCategory").offset(); $("#CategoryDiv").css({ left: cOffset.left + "px", top: cOffset.top + cObj.outerHeight() + "px" }).slideDown("fast"); //给树定位
$("body").bind("mousedown", onBodyDown);//绑定鼠标单击事件
} //隐藏树并解除绑定
function hideMenu() {
$("#CategoryDiv").fadeOut("fast");
$("body").unbind("mousedown", onBodyDown);
} //鼠标单击空白处事件
function onBodyDown(event) {
if (!(event.target.id == "getCategory" || event.target.id == "CategoryDiv" || $(event.target).parents("#CategoryDiv").length > 0)) {
hideMenu();
if ($('#getCategory').val().length === 0) {
$('#getCategory').val("==请选择分类==");
} }
} //加载部分视图,根据父类ID,勾选树中的选项并显示到显式文本框getCategory中
function checkAndDisplay() {
var parentId = $('#categoryId').val(); //注意:这里的父级ID为某个节点的ID
var node = zTree.getNodeByTId(parentId); //显示
var cknodes = zTree.getCheckedNodes(true),
v = "";
for (var j = 0; j < cknodes.length; j++) {
v += cknodes[j].Name + ",";
}
if (v.length > 0) v = v.substring(0, v.length - 1);
$("#getCategory").attr("value", v);
} $(function () {
//点击取消执行父窗口的方法
$('#cancel').on("click", function () {
self.parent.candelEdit();
}); //隐藏树
$("#CategoryDiv").css("display", "none"); //预先加载树
$.ajax({
cache: false,
url: '@Url.Action("LoadAllCategories", "Home")',
type: "GET",
dataType: "text",
success: function (result) {
$.fn.zTree.init($("#tree"), setting, eval('(' + result + ')'));
zTree = $.fn.zTree.getZTreeObj("tree");
zTree.expandAll(true);
},
error: function (xhr, status) {
alert("加载类别失败" + status);
}
}); //加载部分视图内容
$.ajax({
cache: false,
url: '@Url.Action("EditModel", "Home")',
data: {id: '@ViewData["id"].ToString()'},
contentType: 'application/html;charset=utf-8',
type: "GET",
success: function (result) {
$('#content').html(result);
checkAndDisplay();
//限制请选择分类为只读
$('#getCategory').attr('readonly', true);
$.validator.unobtrusive.parse("form");
},
error: function (xhr, status) {
alert("加载内容失败" + status);
}
}); //点击修改发送异步请求到控制器
$('#operate').on("click", "#up", function () {
$.ajax({
cache: false,
url: '@Url.Action("EditCarCategory", "Home")',
type: "POST",
data: $('#editForm').serialize(),
success: function (data) {
if (data.msg) {
self.parent.refreshAfterEdit();
} else {
$('#content').html(data);
checkAndDisplay(); $('#getCategory').attr('readonly', true);//限制请选择分类为只读
$.validator.unobtrusive.parse("form");
}
},
error: function (jqXhr, textStatus, errorThrown) {
alert("出错了 '" + jqXhr.status + "' (状态: '" + textStatus + "', 错误为: '" + errorThrown + "')");
}
});
}); //点击选择分类
$('#content').on("click", "#getCategory", function () {
$("#CategoryDiv").css("display", "block");
showMenu();
}); });
</script>
</head>
<body style="height:550px;">
<div id="wrapper">
<div id="content">
</div>
<div id="operate" style="text-align: center;">
<input type="button" id="up" value="修改" /> <input type="button" id="cancel" value="取消/关闭窗口"/>
</div> <div id="CategoryDiv" style="position: absolute;background-color: #e3e3e3;text-align: center;">
<input type="button" value="展开" onclick="zTree.expandAll(true);"/>
<input type="button" value="折叠" onclick="zTree.expandAll(false);"/>
<hr/> <ul id="tree" class="ztree" style="margin-top:0; width:180px; height: 250px; overflow:auto;margin: 0 auto 0 auto;"></ul>
</div>
</div>
</body>
</html>
批量删除
在前台视图中,把拿到的id拼接成"1,2,3,"形式,然后在控制器方法中,把最后的逗号去掉,再Split成数组,把其中的元素放到List<int>集合中作为服务层方法的参数,对领域模型实施逻辑删除。视图部分在查询和显示部分已有,控制器部分如下:
[HttpPost]
public ActionResult DeleteCarCategories()
{
var strIds = Request["ids"];
strIds = strIds.Substring(0, strIds.Length - 1);
string[] ids = strIds.Split('_');
List<int> list = new List<int>();
foreach (var item in ids)
{
int id = int.Parse(item);
list.Add(id);
}
if (DeleteCarCategoriesByBatch(list) > 0)
{
return Json(new {msg = true});
}
else
{
return Json(new { msg = "no"});
}
}
private int DeleteCarCategoriesByBatch(List<int> ids)
{
var carCategories = CarCategoryRepository.LoadEntities(c => ids.Contains(c.ID)).ToList();
for (int i = 0; i < carCategories.Count(); i++)
{
carCategories[i].DelFlag = (short)DelFlagEnum.Delete;
carCategories[i].SubTime = DateTime.Now;
}
return CarCategoryRepository.SaveChanges();
}
#endregion
MVC无限级分类02,增删改查的更多相关文章
- MVC与EasyUI结合增删改查
构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(9)-MVC与EasyUI结合增删改查 在第八讲中,我们已经做到了怎么样分页.这一讲主要讲增删改查.第六讲的 ...
- 在ASP.NET MVC4中实现同页面增删改查,无弹出框02,增删改查界面设计
在上一篇"在ASP.NET MVC4中实现同页面增删改查,无弹出框01,Repository的搭建"中,已经搭建好了Repository层,本篇就剩下增删改查的界面了......今 ...
- 构建ASP.NET MVC4+EF5+EasyUI+Unity2.x注入的后台管理系统(9)-MVC与EasyUI结合增删改查
系列目录 文章于2016-12-17日重写 在第八讲中,我们已经做到了怎么样分页.这一讲主要讲增删改查.第六讲的代码已经给出,里面包含了增删改,大家可以下载下来看下. 这讲主要是,制作漂亮的工具栏,虽 ...
- 关于MVC工厂模式的增删改查sql存储过程
这里MVC中用到了反射,工厂,泛型,接口 在搭建框架的时候,除了MVC的三层以外,还有泛型的接口层和工厂层 下面是dal层调用sql存储过程,增删改查,dal层继承了接口层,实现了接口层里面的方法 1 ...
- MVC 入门 自动生成 增删改查所有功能
MVC现在版本已经是5了 EF现在最新的应该是6.0.2了 开发工具是 Visual Studio2013 数据库是 SQL Server 2012 这些需要.NET Framework4.5 的 ...
- MVC 中aspx的增删改查
先看总体结构 LInQ #pragma warning disable 1591 //--------------------------------------------------------- ...
- MVC中使用EF增删改查,简单的例子
//这个是分页数据和总页数类 public class SummaryBase<TModel> { public SummaryBase(); public IList<TModel ...
- ASP.NET+MVC+EntityFramework快速实现增删改查
本教程已经录制视频,欢迎大家观看我在CSDN学院录制的课程:http://edu.csdn.net/lecturer/944
- HBASE的Java与Javaweb(采用MVC模式)实现增删改查附带源码
项目文件截图 Java运行截图 package domain; import java.io.IOException; import java.util.ArrayList; import java. ...
随机推荐
- gtk+学习笔记(五)
今天继续做的是昨天那个界面对的优化,直接贴下代码, void click_radio(GtkWidget *widget,gpointer *data) { 3 GtkWidget *dialog; ...
- 将DataTable转换为List,将List转换为DataTable的实现类
将DataTable转换为List,将List转换为DataTable的实现类 public static class DataTableHelper { public static DataTabl ...
- 两类for循环
九.两类for循环 (一)穷举 1.格式 for (初始条件;循环条件 ;循环改变) { for (初始条件;循环条件;循环改变) { for (初始条件;循环条件;循环改变) { if (判断条件) ...
- 安装配置SVN
官网下载地址,下载完之后如果想看到中文,可以下载语言包进行安装,安装之后TortoiseSVN -> Settings -> General -> Language选项中选择:中文( ...
- Vue.js学习笔记(一) - 起步
本篇将简单介绍一下Vue.js,并在Node.js环境下搭建一个简单的Demo. 一.简介 我个人理解,Vue.js是一套前端视图层的框架,它只关心视图展示和数据绑定,它的一些语法与Angular 1 ...
- hadoop集群的搭建(分布式安装)
集群 计算机集群是一种计算机系统,他通过一组松散集成的计算机软件和硬件连接起来高度紧密地协同完成计算工作. 集群系统中的单个计算机通常称为节点,通常通过局域网连接. 集群技术的特点: 1.通过多台计算 ...
- LeetCode 80. 删除排序数组中的重复项 II
LeetCode 80. 删除排序数组中的重复项 II
- 备份恢复-----system表空间损坏
无法进行关库,报错如下 SQL> shutdown immediate ORA-01122: database file 1 failed verification checkORA-01110 ...
- MIT-6.828-JOS-环境搭建
MIT 6.828是操作系统中最经典的一门课程.完成所有的lab就相当于完成了一个迷你的操作系统.我跟的是2018年的课程,课程首页在6.828课程官网.当然所有资料都是英文的,所以难度也不低,这里推 ...
- Python 中的面向对象和异常处理
在之前我们已经说过了 Python 中内置的主要的几种对象类型,(数,字符串,列表,元组和字典).而面向对象的核心人物还没出场呢 .那么我们常说的对象是什么类型的呢,其实他的类型就是“类”.继承封装和 ...