MVC5 网站开发之八 栏目功能 添加、修改和删除

2018-06-22 06:02:51来源:未知 阅读 ()

新老客户大回馈,云服务器低至5折

本次实现栏目的浏览、添加、修改和删除。

栏目一共有三种类型。

  1. 常规栏目-可以添加子栏目,也可以添加内容模型。当不选择内容模型时,不能添加内容。
  2. 单页栏目-栏目只有一个页面,可以设置视图。
  3. 链接栏目-栏目为一个链接,点击后转到相应链接。

在视图中原本栏目的树形显示插件使用Bootstrap TreeView 1.2.0(MVC5 网站开发之六 管理员 2、添加、删除、重置密码、修改密码、列表浏览),后来使用中发现zTree使用起来更习惯,所以更换成zTree了。

目录

MVC5网站开发之一 总体概述

MVC5 网站开发之二 创建项目

MVC5 网站开发之三 数据存储层功能实现

MVC5 网站开发之四 业务逻辑层的架构和基本功能

MVC5 网站开发之五 展示层架构

MVC5 网站开发之六 管理员 1、登录、验证和注销

MVC5 网站开发之六 管理员 2、添加、删除、重置密码、修改密码、列表浏览

MVC5 网站开发之七 用户功能 1、角色的后台管理

MVC5 网站开发之七 用户功能 2 用户添加和浏览

MVC5 网站开发之七 用户功能 3用户资料的修改和删除

MVC5 网站开发之八 栏目功能 添加、修改和删除

 

一、栏目类型

1、栏目类型枚举

在Ninesky.Core项目【右键】添加文件夹“Category”,在文件夹上【右键】添加类“CategoryType”。完成如下代码

using System.ComponentModel.DataAnnotations;

namespace Ninesky.Core.Category
{
    /// <summary>
    /// 栏目类型
    /// </summary>
    public enum CategoryType
    {
        /// <summary>
        /// 常规栏目
        /// </summary>
        [Display(Name = "常规栏目", Description = "可以添加内容,不能添加子栏目")]
        General,
        /// <summary>
        /// 单页栏目
        /// </summary>
        [Display(Name = "单页栏目", Description = "仅有一个页面的栏目")]
        Page,
        /// <summary>
        /// 外部链接
        /// </summary>
        [Display(Name = "外部链接", Description = "点击跳转到指定链接")]
        Link
    }
}

2、枚举的详细信息类

此类有Text、Value、Name、Description四个属性,分别代表枚举的字符串形式、枚举值、标注名称、标注详细说明。前两个是枚举的固有属性,后两个通过添加[Display(Name = "", Description = "")]为枚举添加这两个属性,主要目的方便枚举的中文显示。

Ninesky.Auxiliary【右键】->添加->类,输入类名EnumItemDetails

namespace Ninesky.Auxiliary
{
    /// <summary>
    /// 枚举详细信息
    /// </summary>
    public class EnumItemDetails
    {
        /// <summary>
        /// 枚举的字符串形式
        /// </summary>
        public string Text { get; set; }

        /// <summary>
        /// 枚举值
        /// </summary>
        public int Value { get; set; }

        /// <summary>
        /// 标注名称
        /// </summary>
        public string Name { get; set; }

        /// <summary>
        /// 标注详细说明
        /// </summary>
        public string Description { get; set; }
    }
}

 

3、枚举转换为详细信息列表方法

Ninesky.Auxiliary【右键】->添加->类,输入类名Convert

在类中引入命名空间 System.ComponentModel.DataAnnotationsusing System.Reflection

添加EnumToDetailsList静态方法,将枚举转换为详细信息列表

using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Reflection;

namespace Ninesky.Auxiliary
{
    /// <summary>
    /// 转换类
    /// </summary>
    public class Convert
    {
        /// <summary>
        /// 转换枚举类型为详细信息列表
        /// </summary>
        /// <param name="_enum">枚举类型</param>
        /// <returns></returns>
        public static List<EnumItemDetails> EnumToDetailsList(Enum _enum)
        {
            List<EnumItemDetails> _itemDetails = new List<EnumItemDetails>();
            //字段元数据
            FieldInfo _fileInfo;
            //显示属性
            DisplayAttribute _displayAttribute;
            foreach (var _item in Enum.GetValues(_enum.GetType()))
            {
                try
                {
                    _fileInfo = _item.GetType().GetField(_item.ToString());
                    _displayAttribute = (DisplayAttribute)_fileInfo.GetCustomAttribute(typeof(DisplayAttribute));
                    if (_displayAttribute != null) _itemDetails.Add(new EnumItemDetails() { Text = _item.ToString(), Value = (int)_item, Name = _displayAttribute.Name, Description = _displayAttribute.Description });
                    else _itemDetails.Add(new EnumItemDetails() { Text = _item.ToString(), Value = (int)_item });
                }
                catch
                {
                    _itemDetails.Add(new EnumItemDetails() { Text = _item.ToString(), Value = (int)_item });
                }
            }
            return _itemDetails;
        }
    }
}

 

二、栏目模型类

1、共有信息模型

Ninesky.Core/Category【右键】->添加->类,输入类名Category

引入命名空间System.ComponentModel.DataAnnotations和System.ComponentModel.DataAnnotations.Schema

using System.ComponentModel.DataAnnotations;

namespace Ninesky.Core.Category
{
    /// <summary>
    /// 栏目模型类
    /// </summary>
    public class Category
    {
        [Key]
        public int CategoryID { get; set; }

        /// <summary>
        /// 栏目类型
        /// </summary>
        [Required(ErrorMessage = "{0}必填")]
        [Display(Name = "栏目类型")]
        public CategoryType Type { get; set; }

        /// <summary>
        /// 父栏目ID
        /// </summary>
        [Required(ErrorMessage = "{0}必填")]
        [Display(Name = "父栏目")]
        public int ParentID { get; set; }

        /// <summary>
        /// 父栏目路径格式【0,……,父栏目ID】
        /// </summary>
        [Display(Name = "父栏目路径")]
        public string ParentPath { get; set; }

        /// <summary>
        /// 深度【表示栏目所处层次,根栏目为0,依次类推】
        /// </summary>
        [Required()]
        [Display(Name = "深度")]
        public int Depth { get; set; }

        /// <summary>
        /// 栏目名称
        /// </summary>
        [Required(ErrorMessage = "{0}必填")]
        [StringLength(50, ErrorMessage ="必须少于{1}个字")]
        [Display(Name = "栏目名称")]
        public string Name { get; set; }

        /// <summary>
        /// 栏目说明
        /// </summary>
        [DataType(DataType.MultilineText)]
        [StringLength(1000, ErrorMessage = "必须少于{1}个字")]
        [Display(Name = "栏目说明")]
        public string Description { get; set; }

        /// <summary>
        /// 栏目排序【同级栏目数字越小越靠前】
        /// </summary>
        [Display(Name = "栏目排序")]
        public int Order { get; set; }

        /// <summary>
        /// 打开方式
        /// </summary>
        [Display(Name = "打开方式")]
        public string Target { get; set; }
    }
}

2、常规栏目模型

Ninesky.Core/Category【右键】->添加->类,输入类名CategoryGeneral

using System.ComponentModel.DataAnnotations;


namespace Ninesky.Core.Category
{
    /// <summary>
    /// 常规栏目
    /// </summary>
    public class CategoryGeneral
    {
        [Key]
        public int CategoryGeneralID { get; set; }
        /// <summary>
        /// 栏目ID
        /// </summary>
        [Required()]
        [Display(Name ="栏目ID")]
        public int CategoryID { get; set; }

        /// <summary>
        /// 栏目视图
        /// </summary>
        [Required()]
        [Display(Name = "栏目视图")]
        public string View { get; set; }

        /// <summary>
        /// 内容类型ID【0时表示此栏目无内容模型,不能添加内容】
        /// </summary>
        [Required()]
        [Display(Name = "内容类型")]
        public int ContentTypeID { get; set; }

        /// <summary>
        /// 栏目视图
        /// </summary>
        [Display(Name = "内容视图")]
        public string ContentView { get; set; }

    }
}

3、单页栏目模型

Ninesky.Core/Category【右键】->添加->类,输入类名CategoryPage

using System.ComponentModel.DataAnnotations;

namespace Ninesky.Core.Category
{
    /// <summary>
    /// 单页栏目模型
    /// </summary>
    public class CategoryPage
    {
        [Key]
        public int CategoryPageID { get; set; }

        /// <summary>
        /// 栏目ID
        /// </summary>
        [Required(ErrorMessage = "{0}必填")]
        [Display(Name = "栏目ID")]
        public int CategoryID { get; set; }

        /// <summary>
        /// 栏目视图
        /// </summary>
        [Required(ErrorMessage = "{0}必填")]
        [StringLength(255, ErrorMessage = "必须少于{1}个字")]
        [Display(Name = "栏目视图")]
        public string View { get; set; }

    }
}

4、链接栏目模型

Ninesky.Core/Category【右键】->添加->类,输入类名CategoryLink

using System.ComponentModel.DataAnnotations;

namespace Ninesky.Core.Category
{
    /// <summary>
    /// 外部链接栏目模型
    /// </summary>
    public class CategoryLink
    {
        [Key]
        public int CategoryLinkID { get; set; }

        /// <summary>
        /// 栏目ID
        /// </summary>
        [Required(ErrorMessage = "{0}必填")]
        [Display(Name = "栏目ID")]
        public int CategoryID { get; set; }

        /// <summary>
        /// 链接地址
        /// </summary>
        [Required(ErrorMessage = "{0}必填")]
        [StringLength(255, ErrorMessage = "必须少于{1}个字")]
        [Display(Name = "链接地址")]
        public string Url { get; set; }
    }
}

三、栏目管理类

1、共有信息管理类

Ninesky.Core/Category【右键】->添加->类,输入类CategoryManager

image

从类图中可以看出,该有有添加、查找子栏目、查找栏目列表、查找栏目路径、查找根栏目和更新栏目等方法。没有删除是因为删除方法在基类中就实现了。

添加方法

image

添加栏目有3个重载,分别用于添加常规栏目,添加单页栏目,添加链接栏目。第一个不带参数会在方法中new一个CategoryGeneral出来并调用第二个方法。

更新方法

image

更新有2个重载,分别用于三种栏目的更新。

整个类的代码如下:

using Ninesky.Auxiliary;
using System.Collections.Generic;
using System.Linq;

namespace Ninesky.Core.Category
{
    /// <summary>
    /// 栏目管理
    /// </summary>
    public class CategoryManager : BaseManager<Category>
    {
        #region 添加栏目
        /// <summary>
        /// 添加栏目【Code:2-常规栏目信息不完整,3-单页栏目信息不完整,4-链接栏目信息不完整,5-栏目类型不存在】
        /// </summary>
        /// <param name="category">栏目数据【包含栏目类型对应数据】</param>
        /// <returns></returns>
        public override Response Add(Category category)
        {
            return Add(category, new CategoryGeneral() { CategoryID = category.CategoryID, ContentView = "Index", View = "Index" });
        }

        /// <summary>
        /// 添加栏目
        /// </summary>
        /// <param name="category">基本信息</param>
        /// <param name="general">常规栏目信息</param>
        /// <returns></returns>
        public Response Add(Category category,CategoryGeneral general)
        {
            Response _response = new Response() { Code = 1 };
            _response = base.Add(category);
            general.CategoryID = category.CategoryID;
            var _generalManager = new CategoryGeneralManager();
            _generalManager.Add(general);
            return _response;
        }
        /// <summary>
        /// 添加栏目
        /// </summary>
        /// <param name="category">基本信息</param>
        /// <param name="page">单页栏目信息</param>
        /// <returns></returns>
        public Response Add(Category category, CategoryPage page)
        {
            Response _response = new Response() { Code = 1 };
            _response = base.Add(category);
            page.CategoryID = category.CategoryID;
            var _pageManager = new CategoryPageManager();
            _pageManager.Add(page);
            return _response;
        }

        /// <summary>
        /// 添加栏目
        /// </summary>
        /// <param name="category">基本信息</param>
        /// <param name="link">链接栏目信息</param>
        /// <returns></returns>
        public Response Add(Category category, CategoryLink link)
        {
            Response _response = new Response() { Code = 1 };
            _response = base.Add(category);
            link.CategoryID = category.CategoryID;
            var _linkManager = new CategoryLinkManager();
            _linkManager.Add(link);
            return _response;
        }
        #endregion

        #region 更新栏目

        /// <summary>
        /// 更新栏目
        /// </summary>
        /// <param name="category">栏目</param>
        /// <param name="general">常规信息</param>
        /// <returns></returns>
        public Response Update(Category category, CategoryGeneral general)
        {
            Response _response = new Response() { Code = 1 };
            _response = base.Update(category);
            if (_response.Code == 1)
            {
                general.CategoryID = category.CategoryID;
                var _generalManager = new CategoryGeneralManager();
                if(general.CategoryGeneralID ==0) _response = _generalManager.Add(general);
                else _response = _generalManager.Update(general);
            }
            if (_response.Code == 1) _response.Message = "更新栏目成功!";
            return _response;
        }

        /// <summary>
        /// 更新栏目
        /// </summary>
        /// <param name="category">栏目</param>
        /// <param name="page">单页信息</param>
        /// <returns></returns>
        public Response Update(Category category, CategoryPage page)
        {
            Response _response = new Response() { Code = 1 };
            _response = base.Update(category);
            if (_response.Code == 1)
            {
                page.CategoryID = category.CategoryID;
                var _pageManager = new CategoryPageManager();
                if(page.CategoryPageID ==0) _response = _pageManager.Add(page);
                else _response = _pageManager.Update(page);
            }
            if (_response.Code == 1) _response.Message = "更新栏目成功!";
            return _response;
        }

        /// <summary>
        /// 更新栏目
        /// </summary>
        /// <param name="category">栏目</param>
        /// <param name="link">链接信息</param>
        /// <returns></returns>
        public Response Update(Category category, CategoryLink link)
        {
            Response _response = new Response() { Code = 1 };
            _response = base.Update(category);
            if (_response.Code == 1)
            {
                link.CategoryID = category.CategoryID;
                var _linkManager = new CategoryLinkManager();
                if(link.CategoryLinkID == 0) _response = _linkManager.Add(link);
                else _response = _linkManager.Update(link);
            }
            if (_response.Code == 1) _response.Message = "更新栏目成功!";
            return _response;
        }

        #endregion


        /// <summary>
        /// 查找子栏目
        /// </summary>
        /// <param name="id">栏目ID</param>
        /// <returns></returns>
        public List<Category> FindChildren(int id)
        {
            return Repository.FindList(c => c.ParentID == id, new OrderParam() { Method = OrderMethod.ASC, PropertyName = "Order" }).ToList();
        }

        /// <summary>
        /// 查找根栏目
        /// </summary>
        /// <returns></returns>
        public List<Category> FindRoot()
        {
            return FindChildren(0);
        }

        /// <summary>
        /// 查找栏目路径
        /// </summary>
        /// <param name="id">栏目ID</param>
        /// <returns></returns>
        public List<Category> FindPath(int id)
        {
            List<Category> _categories = new List<Category>();
            var _category = Find(id);
            while (_category != null)
            {
                _categories.Insert(0, _category);
                _category = Find(_category.ParentID);
            }
            return _categories;
        }

        /// <summary>
        /// 查找列表
        /// </summary>
        /// <param name="number">返回数量【0-全部】</param>
        /// <param name="type">栏目类型【null 全部】</param>
        /// <param name="orderParams">【排序参数】</param>
        /// <returns></returns>
        public List<Category> FindList(int number, CategoryType? type,OrderParam[] orderParams)
        {
            List<Category> _categories;
            if (orderParams == null) orderParams = new OrderParam[] { new OrderParam() { Method = OrderMethod.ASC, PropertyName = "ParentPath" }, new OrderParam() { Method = OrderMethod.ASC, PropertyName = "Order" } };
            if (type == null) _categories = base.Repository.FindList(c => true, orderParams, number).ToList();
            else _categories = base.Repository.FindList(c => c.Type == type, orderParams, number).ToList();
            return _categories;

        }
    }

2、常规栏目

Ninesky.Core/Category【右键】->添加->类,输入类名CategoryGeneralManager类里只有一个方法,根据栏目id删除常规栏目,其他两个重载也类似。

using Ninesky.Auxiliary;

namespace Ninesky.Core.Category
{
    /// <summary>
    /// 常规栏目管理
    /// </summary>
    public class CategoryGeneralManager : BaseManager<CategoryGeneral>
    {
        /// <summary>
        /// 删除常规栏目-根据栏目ID
        /// </summary>
        /// <param name="categoryID">栏目ID</param>
        /// <returns></returns>
        public Response DeleteByCategoryID(int categoryID)
        {
            return base.Delete(cg => cg.CategoryID == categoryID);
        }
    }
}

3、单页栏目

Ninesky.Core/Category【右键】->添加->类,输入类名CategoryPageManager

using Ninesky.Auxiliary;

namespace Ninesky.Core.Category
{
    /// <summary>
    /// 单页栏目管理
    /// </summary>
    public class CategoryPageManager : BaseManager<CategoryPage>
    {
        /// <summary>
        /// 删除单页栏目-根据栏目ID
        /// </summary>
        /// <param name="categoryID">栏目ID</param>
        /// <returns></returns>
        public Response DeleteByCategoryID(int categoryID)
        {
            return base.Delete(cp=> cp.CategoryID == categoryID);
        }
    }
}

4、链接栏目

Ninesky.Core/Category【右键】->添加->类,输入类名CategoryLinkManager

using Ninesky.Auxiliary;

namespace Ninesky.Core.Category
{
    /// <summary>
    /// 链接栏目管理
    /// </summary>
    public class CategoryLinkManager : BaseManager<CategoryLink>
    {
        /// <summary>
        /// 删除链接栏目-根据栏目ID
        /// </summary>
        /// <param name="categoryID">栏目ID</param>
        /// <returns></returns>
        public Response DeleteByCategoryID(int categoryID)
        {
            return base.Delete(cl => cl.CategoryID == categoryID);
        }
    }
}

四、栏目效果的实现

Ninesky.Web/Areas/Control/Controllers【右键】->添加->控制器输入控制器名CategoryController

添加私有变量CategoryManager categoryManager,并在构造函数中初始化。

using Ninesky.Auxiliary;
using Ninesky.Core.Category;
using Ninesky.Core.Content;
using Ninesky.Web.Models;
using System.Collections.Generic;
using System.Web.Mvc;

namespace Ninesky.Web.Areas.Control.Controllers
{
    /// <summary>
    /// 栏目控制器
    /// </summary>
    [AdminAuthorize]
    public class CategoryController : Controller
    {
        private CategoryManager categoryManager;
        public CategoryController()
        {
            categoryManager = new CategoryManager();
        }
}

1、左侧树形列表

在控制器中添加Tree方法

/// <summary>
        /// 树形节点数据
        /// </summary>
        /// <returns></returns>
        public ActionResult Tree(bool showIcon=false)
        {
            List<TreeNode> _nodes = new List<TreeNode>();
            //栏目并进行排序使最深的节点排在最前
            var _categories = categoryManager.FindList(0, null, new OrderParam[] { new OrderParam() { Method = OrderMethod.ASC, PropertyName = "ParentPath" }, new OrderParam() { Method = OrderMethod.ASC, PropertyName = "Order" } });
            TreeNode _node;
            //遍历常规栏目
            foreach (var _category in _categories)
            {
                _node = new TreeNode() { pId = _category.ParentID, id = _category.CategoryID, name = _category.Name, url = Url.Action("Modify", "Category", new { id = _category.CategoryID }) };
                if (showIcon) {
                    switch (_category.Type)
                    {
                        case CategoryType.General:
                            _node.icon = Url.Content("~/Content/Images/folder.png");
                            break;
                        case CategoryType.Page:
                            _node.icon = Url.Content("~/Content/Images/page.png");
                            break;
                        case CategoryType.Link:
                            _node.icon = Url.Content("~/Content/Images/link.png");
                            break;
                    }
                }
                _nodes.Add(_node);
            }

            return Json(_nodes);
        }

在Ninesky.Web/Areas/Control/Views/Category/[右键] 添加视图-视图名称为SideNavPartialView,类型为分部视图

<div class="panel panel-default">
    <div class="panel-heading">
        <div class="panel-title"><span class="glyphicon glyphicon-lock"></span> 栏目列表</div>
    </div>
    <div class="panel-body">
        <ul id="categorytree" class="ztree" style="margin:0;padding:0"></ul>
    </div>
</div>
<script type="text/javascript">
    $(document).ready(function () {
        //父栏目选择框 zTree
        var zTreeCategory;
        // zTree 的参数配置
        var setting = {
            data: {
                simpleData: {
                    enable: true,
                    idKey: "id",
                    pIdKey: "pId",
                    rootPId: 0
                },
                key: {
                    name: "name"
                }
            }
        };
        $.post("@Url.Action("Tree", "Category",new { showIcon=true })", null, function (data) {
            zTreeCategory = $.fn.zTree.init($("#categorytree"), setting, data);
        }, "json");
        
    });
</script>

2、添加栏目

添加栏目界面如下图:

image

视图中有两个标签:“基本资料”标签显示栏目的共有信息(Category类),“高级设置”标签显示栏目附加信息(根据栏目类型显示常规、单页或链接栏目信息)。这里一共涉及4个视图。

 

  • 栏目类型选择框数据

在在CategoryController添加Add方法public ActionResult DropdownTree(),该方法返回Json类型可以做父栏目的栏目树。

/// <summary>
        /// 下拉树【常规栏目】
        /// </summary>
        /// <returns></returns>
        public ActionResult DropdownTree()
        {
            //栏目并进行排序使最深的节点排在最前
            var _categories = categoryManager.FindList(0, CategoryType.General, new OrderParam[] { new OrderParam() { Method = OrderMethod.ASC, PropertyName = "ParentPath" }, new OrderParam() { Method = OrderMethod.ASC, PropertyName = "Order" } });
            List<TreeNode> _nodes = new List<TreeNode>();
            //遍历常规栏目
            foreach (var _category in _categories)
            {
                _nodes.Add(new TreeNode() { pId = _category.ParentID, id = _category.CategoryID, name = _category.Name });
            }

            return Json(_nodes);
        }
  • 栏目共有信息(页面视图)

在CategoryController添加Add方法,返回页面视图

/// <summary>
        /// 添加栏目
        /// </summary>
        /// <param name="id">父栏目ID</param>
        /// <returns></returns>
        public ActionResult Add(int? id)
        {
            Category _category = new Category() { ParentID = 0 };
            if (id != null && id>0 )
            {
                var _parent = categoryManager.Find((int)id);
                if (_parent != null && _parent.Type == CategoryType.General) _category.ParentID = (int)id;
            }
            return View(_category);
        }

右键添加视图,模型选择Category

@model Ninesky.Core.Category.Category

@{
    ViewBag.Title = "添加栏目";
}
@section SideNav{@Html.Partial("SideNavPartialView")}

<ol class="breadcrumb">
    <li><span class="glyphicon glyphicon-home"></span>  @Html.ActionLink("首页", "Index", "Home")</li>
    <li>@Html.ActionLink("栏目管理", "Index", "Category")</li>
    <li class="active">添加栏目</li>
</ol>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()
    <div class="form-horizontal">
        @Html.ValidationSummary(false, "", new { @class = "text-danger" })

        <ul id="myTabs" class="nav nav-tabs" role="tablist">
            <li role="presentation"><a href="#base" role="tab" data-toggle="tab" aria-controls="base">基本资料</a></li>
            <li id="tabadvanced" role="presentation"><a href="#advanced" role="tab" data-toggle="tab" aria-controls="advanced">高级设置</a></li>
        </ul>
        <div class="tab-content">
            <div role="tabpanel" class="tab-pane active" id="base">
                <br />
                <br />
                <div class="form-group">
                    @Html.LabelFor(model => model.ParentID, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.HiddenFor(model => model.ParentID)
                        <div class="input-group" style="width:280px">
                            <input id="ParentName" type="text" class="form-control" readonly />
                            <ul class="dropdown-menu dropdown-menu-left ztree" style="display:none;width:100%" id="parenttree"></ul>
                            <div class="input-group-btn open">
                                <button id="btn-CategoryID" type="button" class="btn btn-default"><span class="caret"></span></button>
                            </div>
                        </div>
                        @Html.ValidationMessageFor(model => model.ParentID, "", new { @class = "text-danger" })
                    </div>
                </div>

                <div class="form-group">
                    @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
                        @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
                    </div>
                </div>

                <div class="form-group">
                    @Html.LabelFor(model => model.Type, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EnumDropDownListFor(model => model.Type, new { @class = "form-control" })
                        @Html.ValidationMessageFor(model => model.Type, "", new { @class = "text-danger" })
                    </div>
                </div>

                <div class="form-group">
                    @Html.LabelFor(model => model.Description, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.Description, new { htmlAttributes = new { @class = "form-control" } })
                        @Html.ValidationMessageFor(model => model.Description, "", new { @class = "text-danger" })
                    </div>
                </div>

                <div class="form-group">
                    @Html.LabelFor(model => model.Order, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.Order, new { htmlAttributes = new { @class = "form-control" } })
                        @Html.ValidationMessageFor(model => model.Order, "", new { @class = "text-danger" })
                    </div>
                </div>

                <div class="form-group">
                    @Html.LabelFor(model => model.Target, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.Target, new { htmlAttributes = new { @class = "form-control" } })
                        @Html.ValidationMessageFor(model => model.Target, "", new { @class = "text-danger" })
                    </div>
                </div>

            </div>
            <div role="tabpanel" class="tab-pane" id="advanced">
                1
            </div>
        </div>

        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="保存" class="btn btn-default" />
            </div>
        </div>
    </div>
}


<script type="text/javascript">
    //显示隐藏标签页
    function showTabBySelect() {
        switch ($("#Type").find("option:selected").text()) {
            case "常规栏目":
                $("#advanced").load("@Url.Action("AddGeneral")");
                break;
            case "单页栏目":
                $("#advanced").load("@Url.Action("AddPage")");
                break;
            case "外部链接":
                $("#advanced").load("@Url.Action("AddLink")");
                break;
        }
    }
    $(document).ready(function () {
        showTabBySelect();
        $("#Type").change(function () {
            showTabBySelect();
        });
        //父栏目选择框 zTree
        var zTreeObj;
        // zTree 的参数配置
        var setting = {
            data: {
                simpleData: {
                    enable: true,
                    idKey: "id",
                    pIdKey: "pId",
                    rootPId: 0
                },
                key: {
                    name: "name"
                }
            },
            callback: {
                onClick: function (event, treeId, treeNode) {
                    $("#ParentID").val(treeNode.id);
                    $("#ParentName").val(treeNode.name);
                    $("#parenttree").slideUp();
                }
            }
        };
        // zTree 的数据属性,
        $.post("@Url.Action("DropdownTree")", null, function (data) {
            zTreeObj = $.fn.zTree.init($("#parenttree"), setting, data);
            var newNode = { name: "", id: "0", pId: "-1" };
            newNode = zTreeObj.addNodes(null, 0, newNode);
            var node = zTreeObj.getNodeByParam("id", $("#ParentID").val(), null);
            if (node != null) {
                $("#ParentName").val(node.name);
                zTreeObj.selectNode(node);
            }
        }, "json");

    });
    $("#btn-CategoryID").click(function () { $("#parenttree").slideDown(); });
    //父栏目选择框 zTree
</script>

 

  • 常规栏目(分部视图)

在控制器中添加“AddGeneral”方法,返回分部视图。方法中通过ViewBag.ContentTypeItems向前台传递内容类型列表

/// <summary>
        /// 常规栏目信息
        /// </summary>
        /// <returns></returns>
        public ActionResult AddGeneral()
        {
            var _general = new CategoryGeneral() { ContentView="Index", View="Index" };
            List<SelectListItem> _contentTypeItems = new List<SelectListItem>() { new SelectListItem() { Value = "0", Text = "", Selected = true } };
            ContentTypeManager _contentTypeManager = new ContentTypeManager();
            var _contentTypes = _contentTypeManager.FindList();
            foreach(var contentType in _contentTypes)
            {
                _contentTypeItems.Add(new SelectListItem() { Value= contentType.ContentTypeID.ToString(), Text= contentType.Name });
            }
            ViewBag.ContentTypeItems = _contentTypeItems;
            return PartialView(_general);
        }

右键添加视图,模型CategoryGeneral,分部视图

@model Ninesky.Core.Category.CategoryGeneral
<br />
<br />
<div class="form-group">
    @Html.LabelFor(model => model.View, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(model => model.View, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => model.View, "", new { @class = "text-danger" })
    </div>
</div>

<div class="form-group">
    @Html.LabelFor(model => model.ContentTypeID, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.DropDownListFor(model => model.ContentTypeID,(IEnumerable<SelectListItem>)ViewBag.ContentTypeItems, new { @class = "form-control" })
        @Html.ValidationMessageFor(model => model.ContentTypeID, "", new { @class = "text-danger" })
    </div>
</div>

<div class="form-group">
    @Html.LabelFor(model => model.ContentView, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(model => model.ContentView, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => model.ContentView, "", new { @class = "text-danger" })
    </div>
</div>
  • 单页栏目的(分部视图)

同常规栏目,代码如下

/// <summary>
        /// 添加单页栏目
        /// </summary>
        /// <returns></returns>
        public ActionResult AddPage()
        {
            var _page = new CategoryPage() { View="Index" };
            return PartialView(_page);

        }
@model Ninesky.Core.Category.CategoryPage

<br />
<br />
<div class="form-group">
    @Html.LabelFor(model => model.View, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(model => model.View, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => model.View, "", new { @class = "text-danger" })
    </div>
</div>
  • 链接栏目的分部视图

同上,代码如下:

/// <summary>
        /// 添加外部链接
        /// </summary>
        /// <returns></returns>
        public ActionResult AddLink()
        {
            var _link = new CategoryLink() { Url="http://" };
            return PartialView(_link);
        }
@model Ninesky.Core.Category.CategoryLink

<br />
<br />
<div class="form-group">
    @Html.LabelFor(model => model.Url, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(model => model.Url, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => model.Url, "", new { @class = "text-danger" })
    </div>
</div>

这几个action代码都很简单,只有在AddGeneral中进行了内容内容类型的查询

然后实现添加栏目后台处理代码

[HttpPost]
        [ValidateAntiForgeryToken()]
        public ActionResult Add()
        {
            Category _category = new Category();
            if (TryUpdateModel(_category, new string[] { "Type", "ParentID", "Name", "Description", "Order", "Target" }))
            {
                //检查父栏目
                if (_category.ParentID > 0)
                {
                    var _parentCategory = categoryManager.Find(_category.ParentID);
                    if (_parentCategory == null) ModelState.AddModelError("ParentID", "父栏目不存在,请刷新后重新添加");
                    else if (_parentCategory.Type != CategoryType.General) ModelState.AddModelError("ParentID", "父栏目不允许添加子栏目");
                    else
                    {
                        _category.ParentPath = _parentCategory.ParentPath + "," + _parentCategory.CategoryID;
                        _category.Depth = _parentCategory.Depth + 1;
                    }
                }
                else
                {
                    _category.ParentPath = "0";
                    _category.Depth = 0;
                }
                //栏目基本信息保存
                Response _response = new Auxiliary.Response() { Code = 0, Message = "初始失败信息" };
                //根据栏目类型进行处理
                switch (_category.Type)
                {
                    case CategoryType.General:
                        var _general = new CategoryGeneral();
                        TryUpdateModel(_general);
                        _response = categoryManager.Add(_category, _general);
                        break;
                    case CategoryType.Page:
                        var _page = new CategoryPage();
                        TryUpdateModel(_page);
                        _response = categoryManager.Add(_category, _page);
                        break;
                    case CategoryType.Link:
                        var _link = new CategoryLink();
                        TryUpdateModel(_link);
                        _response = categoryManager.Add(_category, _link);
                        break;
                }
                if (_response.Code == 1) return View("Prompt", new Ninesky.Web.Models.Prompt() { Title = "添加栏目成功", Message = "添加栏目【" + _category.Name + "】成功" });
                else return View("Prompt", new Ninesky.Web.Models.Prompt() { Title = "添加失败", Message = "添加栏目【" + _category.Name + "】时发生系统错误,未能保存到数据库,请重试" });
            }
            return View(_category);
        }

方法中使用TryUpdateModel根据需要的字段绑定模型,然后判断父栏目是否存在、是否可以添加子栏目。最后根据栏目类型使用TryUpdateModel绑定常规栏目,单页栏目或链接栏目信息并保存到数据库。

3、修改栏目

修改栏目和添加栏目方式基本类似,这里只贴代码。注意后台处理时相比添加处理方法要验证选择地父栏目是否是其本身或其子栏目。

#region 修改

        /// <summary>
        /// 修改
        /// </summary>
        /// <param name="id">栏目ID</param>
        /// <returns></returns>
        public ActionResult Modify(int id)
        {
            var _category = categoryManager.Find(id);
            if (_category == null) return View("Prompt", new Prompt() { Title="错误", Message="栏目不存在!" });
            return View(_category);
        }

        /// <summary>
        /// 修改
        /// </summary>
        /// <returns></returns>
        [HttpPost]
        [ValidateAntiForgeryToken]
        public ActionResult Modify(int id, FormCollection form)
        {
            Category _category = categoryManager.Find(id);
            if (_category == null) return View("Prompt", new Prompt() { Title = "错误", Message = "栏目不存在!" });
            if(TryUpdateModel(_category, new string[] { "Type", "ParentID", "Name", "Description", "Order", "Target" }))
            {
                //检查父栏目
                if (_category.ParentID > 0)
                {
                    var _parentCategory = categoryManager.Find(_category.ParentID);
                    if (_parentCategory == null) ModelState.AddModelError("ParentID", "父栏目不存在,请刷新后重新添加");
                    else if (_parentCategory.Type != CategoryType.General) ModelState.AddModelError("ParentID", "父栏目不允许添加子栏目");
                    else if(_parentCategory.ParentPath.IndexOf(_category.ParentPath)>=0) ModelState.AddModelError("ParentID", "父栏目不能是其本身或其子栏目");
                    else
                    {
                        _category.ParentPath = _parentCategory.ParentPath + "," + _parentCategory.CategoryID;
                        _category.Depth = _parentCategory.Depth + 1;
                    }
                }
                else
                {
                    _category.ParentPath = "0";
                    _category.Depth = 0;
                }
                //栏目基本信息保存
                Response _response = new Auxiliary.Response() { Code = 0, Message = "初始失败信息" };
                //根据栏目类型进行处理
                switch (_category.Type)
                {
                    case CategoryType.General:
                        var _generalManager = new CategoryGeneralManager();
                        var _general = _generalManager.Find(g=>g.CategoryID == id);
                        if (_general == null) _general = new CategoryGeneral() { CategoryID = id, View = "Index", ContentView = "Index" };
                        if(TryUpdateModel(_general)) _response = categoryManager.Update(_category, _general);
                        break;
                    case CategoryType.Page:
                        var _pageManager = new CategoryPageManager();
                        var _page = _pageManager.Find(p => p.CategoryID == id);
                        if (_page == null) _page = new CategoryPage() { CategoryID = id, View = "index" };
                        if (TryUpdateModel(_page)) _response = categoryManager.Update(_category, _page);
                        break;
                    case CategoryType.Link:
                        var _linkManager = new CategoryLinkManager();
                        var _link = _linkManager.Find(l => l.CategoryID == id);
                        if (_link == null) _link = new CategoryLink() { CategoryID = id, Url = "http://" };
                        if(TryUpdateModel(_link))_response = categoryManager.Update(_category, _link);
                        break;
                }
                if (ModelState.IsValid)
                {
                    if (_response.Code == 1) return View("Prompt", new Ninesky.Web.Models.Prompt() { Title = "修改栏目成功", Message = "修改栏目【" + _category.Name + "】成功" });
                    else return View("Prompt", new Ninesky.Web.Models.Prompt() { Title = "修改栏目失败", Message = "修改栏目【" + _category.Name + "】时发生系统错误,未能保存到数据库,请重试" });
                }
            }
            return View(_category);
        }

        /// <summary>
        /// 常规栏目
        /// </summary>
        /// <param name="id">栏目ID</param>
        /// <returns></returns>
        public ActionResult ModifyGeneral(int id)
        {
            List<SelectListItem> _contentTypeItems = new List<SelectListItem>() { new SelectListItem { Text = "", Value = "0" } };
            ContentTypeManager _contentTypeManager = new ContentTypeManager();
            var _contentTypes = _contentTypeManager.FindList();
            foreach (var contentType in _contentTypes)
            {
                _contentTypeItems.Add(new SelectListItem() { Value = contentType.ContentTypeID.ToString(), Text = contentType.Name });
            }
            ViewBag.ContentTypeItems = _contentTypeItems;
            var _generalManager = new CategoryGeneralManager();
            var _general = _generalManager.Find(g => g.CategoryID == id);
            if (_general == null) _general = new CategoryGeneral() { ContentView = "index", View = "index" }; 
            return PartialView(_general);
        }

        /// <summary>
        /// 单页栏目
        /// </summary>
        /// <param name="id">栏目ID</param>
        /// <returns></returns>
        public ActionResult ModifyPage(int id)
        {
            var _pageManager = new CategoryPageManager();
            var _page = _pageManager.Find(g => g.CategoryID == id);
            if (_page == null) _page = new CategoryPage() { View = "index" };
            return PartialView(_page);
        }

        /// <summary>
        /// 链接栏目
        /// </summary>
        /// <param name="id">栏目ID</param>
        /// <returns></returns>
        public ActionResult ModifyLink(int id)
        {
            var _linkManager = new CategoryLinkManager();
            var _link = _linkManager.Find(g => g.CategoryID == id);
            if (_link == null) _link = new CategoryLink() { Url = "http://" };
            return PartialView(_link);
        }


        #endregion

Modify 视图

@model Ninesky.Core.Category.Category
@{
    ViewBag.Title = "修改栏目";
}
@section SideNav{@Html.Partial("SideNavPartialView")}
<ol class="breadcrumb">
    <li><span class="glyphicon glyphicon-home"></span>  @Html.ActionLink("首页", "Index", "Home")</li>
    <li>@Html.ActionLink("栏目管理", "Index", "Category")</li>
    <li class="active">修改栏目</li>
</ol>

@using (Html.BeginForm())
{
    @Html.AntiForgeryToken()

    <div class="form-horizontal">
        @Html.ValidationSummary(true, "", new { @class = "text-danger" })
        @Html.HiddenFor(model => model.CategoryID)
        <ul id="myTabs" class="nav nav-tabs" role="tablist">
            <li role="presentation"><a href="#base" role="tab" data-toggle="tab" aria-controls="base">基本资料</a></li>
            <li id="tabadvanced" role="presentation"><a href="#advanced" role="tab" data-toggle="tab" aria-controls="advanced">高级设置</a></li>
        </ul>
        <div class="tab-content">
            <div role="tabpanel" class="tab-pane active" id="base">
                <br />
                <br />
                <div class="form-group">
                    @Html.LabelFor(model => model.ParentID, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.HiddenFor(model => model.ParentID)
                        <div class="input-group" style="width:280px">
                            <input id="ParentName" type="text" class="form-control" readonly />
                            <ul class="dropdown-menu dropdown-menu-left ztree" style="display:none;width:100%" id="parenttree"></ul>
                            <div class="input-group-btn open">
                                <button id="btn-CategoryID" type="button" class="btn btn-default"><span class="caret"></span></button>
                            </div>
                        </div>
                        @Html.ValidationMessageFor(model => model.ParentID, "", new { @class = "text-danger" })
                    </div>
                </div>
                <div class="form-group">
                    @Html.LabelFor(model => model.Name, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.Name, new { htmlAttributes = new { @class = "form-control" } })
                        @Html.ValidationMessageFor(model => model.Name, "", new { @class = "text-danger" })
                    </div>
                </div>
                <div class="form-group">
                    @Html.LabelFor(model => model.Type, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EnumDropDownListFor(model => model.Type, new { @class = "form-control" })
                        @Html.ValidationMessageFor(model => model.Type, "", new { @class = "text-danger" })
                    </div>
                </div>
                <div class="form-group">
                    @Html.LabelFor(model => model.Description, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.Description, new { htmlAttributes = new { @class = "form-control" } })
                        @Html.ValidationMessageFor(model => model.Description, "", new { @class = "text-danger" })
                    </div>
                </div>
                <div class="form-group">
                    @Html.LabelFor(model => model.Order, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.Order, new { htmlAttributes = new { @class = "form-control" } })
                        @Html.ValidationMessageFor(model => model.Order, "", new { @class = "text-danger" })
                    </div>
                </div>
                <div class="form-group">
                    @Html.LabelFor(model => model.Target, htmlAttributes: new { @class = "control-label col-md-2" })
                    <div class="col-md-10">
                        @Html.EditorFor(model => model.Target, new { htmlAttributes = new { @class = "form-control" } })
                        @Html.ValidationMessageFor(model => model.Target, "", new { @class = "text-danger" })
                    </div>
                </div>
            </div>
            <div role="tabpanel" class="tab-pane" id="advanced">

            </div>
        </div>
        <div class="form-group">
            <div class="col-md-offset-2 col-md-10">
                <input type="submit" value="保存" class="btn btn-default" />
            </div>
        </div>
    </div>
}
<script type="text/javascript">
    //显示隐藏标签页
    function showTabBySelect() {
        switch ($("#Type").find("option:selected").text()) {
            case "常规栏目":
                $("#advanced").load("@Url.Action("ModifyGeneral",new { id= Model.CategoryID })");
                break;
            case "单页栏目":
                $("#advanced").load("@Url.Action("ModifyPage", new { id = Model.CategoryID })");
                break;
            case "外部链接":
                $("#advanced").load("@Url.Action("ModifyLink", new { id = Model.CategoryID })");
                break;
        }
    }
    $(document).ready(function () {
        showTabBySelect();
        $("#Type").change(function () {
            showTabBySelect();
        });
        //父栏目选择框 zTree
        var zTreeObj;
        // zTree 的参数配置
        var setting = {
            data: {
                simpleData: {
                    enable: true,
                    idKey: "id",
                    pIdKey: "pId",
                    rootPId: 0
                },
                key: {
                    name: "name"
                }
            },
            callback: {
                onClick: function (event, treeId, treeNode) {
                    $("#ParentID").val(treeNode.id);
                    $("#ParentName").val(treeNode.name);
                    $("#parenttree").slideUp();
                }
            }
        };
        // zTree 的数据属性,
        $.post("@Url.Action("DropdownTree")", null, function (data) {
            zTreeObj = $.fn.zTree.init($("#parenttree"), setting, data);
            var newNode = { name: "", id: "0", pId: "-1" };
            newNode = zTreeObj.addNodes(null, 0, newNode);
            var node = zTreeObj.getNodeByParam("id", $("#ParentID").val(), null);
            if (node != null) {
                $("#ParentName").val(node.name);
                zTreeObj.selectNode(node);
            }
        }, "json");

    });
    $("#btn-CategoryID").click(function () { $("#parenttree").slideDown(); });
    //父栏目选择框 zTree 结束
    </script>

ModifyGeneral分部视图

@model Ninesky.Core.Category.CategoryGeneral
<br />
<br />
<div class="form-group">
    @Html.LabelFor(model => model.View, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(model => model.View, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => model.View, "", new { @class = "text-danger" })
    </div>
</div>

<div class="form-group">
    @Html.LabelFor(model => model.ContentTypeID, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.DropDownListFor(model => model.ContentTypeID,(IEnumerable<SelectListItem>)ViewBag.ContentTypeItems, new { @class = "form-control" })
        @Html.ValidationMessageFor(model => model.ContentTypeID, "", new { @class = "text-danger" })
    </div>
</div>

<div class="form-group">
    @Html.LabelFor(model => model.ContentView, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(model => model.ContentView, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => model.ContentView, "", new { @class = "text-danger" })
    </div>
</div>

ModifyPage分部视图

@model Ninesky.Core.Category.CategoryPage

<br />
<br />
<div class="form-group">
    @Html.LabelFor(model => model.View, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(model => model.View, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => model.View, "", new { @class = "text-danger" })
    </div>
</div>

ModifyLink分部视图

@model Ninesky.Core.Category.CategoryLink

<br />
<br />
<div class="form-group">
    @Html.LabelFor(model => model.Url, htmlAttributes: new { @class = "control-label col-md-2" })
    <div class="col-md-10">
        @Html.EditorFor(model => model.Url, new { htmlAttributes = new { @class = "form-control" } })
        @Html.ValidationMessageFor(model => model.Url, "", new { @class = "text-danger" })
    </div>
</div>

4、删除栏目

在Category控制器中添加Delete(int id),方法的方式为[httppost],返回json类型提示。在方法中要先确定栏目是否存在,然后判断是否有子栏目,如果有子栏目则不能删除(其实还应该判断是否有内容),如无子栏目则连带栏目类型对应的数据一起删除。

/// <summary>
        /// 删除栏目
        /// </summary>
        /// <param name="id">栏目ID</param>
        /// <returns></returns>
        [HttpPost]
        public ActionResult Delete(int id)
        {
            Response _resp = new Auxiliary.Response();
            var _category = categoryManager.Find(id);
            if (_category == null)
            {
                _resp.Code = 0;
                _resp.Message = "栏目不存在";
            }
            else
            {
                if (categoryManager.Count(c=>c.ParentID == _category.CategoryID)>0)
                {
                    _resp.Code = 0;
                    _resp.Message = "该栏目栏目有子栏目,请先删除子栏目";
                }
                else
                {
                    switch(_category.Type)
                    {
                        case CategoryType.General:
                            new CategoryGeneralManager().DeleteByCategoryID(_category.CategoryID);
                            break;
                        case CategoryType.Page:
                            new CategoryPageManager().DeleteByCategoryID(_category.CategoryID);
                            break;
                        case CategoryType.Link:
                            new CategoryLinkManager().DeleteByCategoryID(_category.CategoryID);
                            break;
                    }
                    _resp = categoryManager.Delete(_category);
                }
            }
            return Json(_resp);

        }

然后打开Modify视图。在保存按钮旁添加一个删除按钮,如下图

image

然后在js代码最后添加删除js代码,如下图:

image

 

完成后效果如下图:

image

==========================================

代码下载请见http://www.cnblogs.com/mzwhj/p/5729848.html

标签:

版权申明:本站文章部分自网络,如有侵权,请联系:west999com@outlook.com
特别注意:本站所有转载文章言论不代表本站观点,本站所提供的摄影照片,插画,设计作品,如需使用,请与原作者联系,版权归原作者所有

上一篇:验证码在后台的编写,并实现点击验证码图片时时发生更新

下一篇:【MVC拾遗】MVC的单元测试简单学习总结