.Net Core MVC 网站开发(Ninesky) 2.4、添加栏目与异步方法

在2.3中完成依赖注入后,这次主要实现栏目的添加功能。按照前面思路栏目有三种类型,常规栏目即可以添加子栏目也可以选择是否添加内容,内容又可以分文章或其他类型,所以还要添加一个模块功能。这次主要实现栏目的添加,附带实现模块列表功能,并将业务逻辑层的功能都实现了异步方法。

先来个完成后的界面吧。

image

一、业务逻辑层异步方法

.net Core中异步方法很简单,只需要Task、async、await三个关键字就行。比如要实现统计记录数异步方法,只要给方法添加关键字async,然后返回Task类型,并在方法中使用await调用异步方法就可以。

public async Task<int> CountAsync(Expression<Func<T, bool>> predicate)
        {
            return await _dbContext.Set<T>().CountAsync(predicate);
        }

一般在异步方法中也是调用异步方法,如果调用同步方法,如果直接调用同步方法也没问题,但是编译器会显示警告,看着警告烦可以Task.FromResult,像下面这样子。

public virtual async Task<IQueryable<T>> FindListAsync()
        {
            IQueryable<T> result = _dbContext.Set<T>();
            return await Task.FromResult(result);
        }

基本内容都差不多,下面直接贴代码。

1、异步接口

在Ninesky.InterfaceBase中打开InterfaceBaseService文件。添加接口异步的方法,结果如下:

using Ninesky.Models;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace Ninesky.InterfaceBase
{
    /// <summary>
    ///  服务基础接口
    /// </summary>
    public interface InterfaceBaseService<T> where T:class
    {

        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="entity">实体</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>添加的记录数[isSave=true时有效]</returns>
        int Add(T entity, bool isSave = true);

        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="entity">实体</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>添加的记录数[isSave=true时有效]</returns>
        Task<int> AddAsync(T entity, bool isSave = true);

        /// <summary>
        /// 添加[批量]
        /// </summary>
        /// <param name="entities">实体</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>添加的记录数</returns>
        int AddRange(T[] entities, bool isSave = true);
        /// <summary>
        /// 添加[批量]
        /// </summary>
        /// <param name="entities">实体</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>添加的记录数[isSave=true时有效]</returns>
        Task<int> AddRangeAsync(T[] entities, bool isSave = true);

        /// <summary>
        /// 查询记录数
        /// </summary>
        /// <param name="predicate">查询条件</param>
        /// <returns>记录数</returns>
        int Count(Expression<Func<T, bool>> predicate);
        
        /// <summary>
        /// 查询记录数
        /// </summary>
        /// <param name="predicate">查询条件</param>
        /// <returns>记录数</returns>
        Task<int> CountAsync(Expression<Func<T, bool>> predicate);

        /// <summary>
        /// 查询是否存在
        /// </summary>
        /// <param name="predicate">查询条件</param>
        /// <returns>是否存在</returns>
        bool Exists(Expression<Func<T, bool>> predicate);

        /// <summary>
        /// 查询是否存在
        /// </summary>
        /// <param name="predicate">查询条件</param>
        /// <returns>是否存在</returns>
        Task<bool> ExistsAsync(Expression<Func<T, bool>> predicate);

        /// <summary>
        /// 查找
        /// </summary>
        /// <param name="Id">主键</param>
        /// <returns></returns>
        T Find(int Id);

        /// <summary>
        /// 查找
        /// </summary>
        /// <param name="Id">主键</param>
        /// <returns></returns>
        Task<T> FindAsync(int Id);

        /// <summary>
        /// 查找
        /// </summary>
        /// <param name="keyValues">主键</param>
        /// <returns></returns>
        T Find(object[] keyValues);

        /// <summary>
        /// 查找
        /// </summary>
        /// <param name="keyValues">主键</param>
        /// <returns></returns>
        Task<T> FindAsync(object[] keyValues);

        /// <summary>
        /// 查找
        /// </summary>
        /// <param name="predicate">查询条件</param>
        /// <returns></returns>
        T Find(Expression<Func<T, bool>> predicate);

        /// <summary>
        /// 查找
        /// </summary>
        /// <param name="predicate">查询条件</param>
        /// <returns></returns>
        Task<T> FindAsync(Expression<Func<T, bool>> predicate);

        /// <summary>
        /// 查询
        /// </summary>
        /// <returns>实体列表</returns>
        IQueryable<T> FindList();

        /// <summary>
        /// 查询
        /// </summary>
        /// <returns>实体列表</returns>
        Task<IQueryable<T>> FindListAsync();

        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="predicate">查询条件</param>
        /// <returns>实体列表</returns>
        IQueryable<T> FindList(Expression<Func<T, bool>> predicate);

        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="predicate">查询条件</param>
        /// <returns>实体列表</returns>
        Task<IQueryable<T>> FindListAsync(Expression<Func<T, bool>> predicate);

        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="number">返回记录数</param>
        /// <param name="predicate">查询条件</param>
        /// <returns>实体列表</returns>
        IQueryable<T> FindList(int number, Expression<Func<T, bool>> predicate);

        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="number">返回记录数</param>
        /// <param name="predicate">查询条件</param>
        /// <returns>实体列表</returns>
        Task<IQueryable<T>> FindListAsync(int number, Expression<Func<T, bool>> predicate);

        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="number">显示数量[小于等于0-不启用]</param>
        /// <typeparam name="TKey">排序字段</typeparam>
        /// <param name="predicate">查询条件</param>
        /// <param name="keySelector">排序</param>
        /// <param name="isAsc">正序</param>
        /// <returns></returns>
        IQueryable<T> FindList<TKey>(int number, Expression<Func<T, bool>> predicate, Expression<Func<T, TKey>> keySelector, bool isAsc);

        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="number">显示数量[小于等于0-不启用]</param>
        /// <typeparam name="TKey">排序字段</typeparam>
        /// <param name="predicate">查询条件</param>
        /// <param name="keySelector">排序</param>
        /// <param name="isAsc">正序</param>
        /// <returns></returns>
        Task<IQueryable<T>> FindListAsync<TKey>(int number, Expression<Func<T, bool>> predicate, Expression<Func<T, TKey>> keySelector, bool isAsc);

        /// <summary>
        /// 查询[分页]
        /// </summary>
        /// <typeparam name="TKey">排序属性</typeparam>
        /// <param name="predicate">查询条件</param>
        /// <param name="keySelector">排序</param>
        /// <param name="isAsc">是否正序</param>
        /// <param name="paging">分页数据</param>
        /// <returns></returns>
        Paging<T> FindList<TKey>(Expression<Func<T, bool>> predicate, Expression<Func<T, TKey>> keySelector, bool isAsc, Paging<T> paging);

        /// <summary>
        /// 查询[分页]
        /// </summary>
        /// <typeparam name="TKey">排序属性</typeparam>
        /// <param name="predicate">查询条件</param>
        /// <param name="keySelector">排序</param>
        /// <param name="isAsc">是否正序</param>
        /// <param name="paging">分页数据</param>
        /// <returns></returns>
        Task<Paging<T>> FindListAsync<TKey>(Expression<Func<T, bool>> predicate, Expression<Func<T, TKey>> keySelector, bool isAsc, Paging<T> paging);

        /// <summary>
        /// 查询[分页]
        /// </summary>
        /// <typeparam name="TKey">排序属性</typeparam>
        /// <param name="predicate">查询条件</param>
        /// <param name="keySelector">排序</param>
        /// <param name="isAsc">是否正序</param>
        /// <param name="pageIndex">当前页</param>
        /// <param name="pageSize">每页记录数</param>
        /// <returns></returns>
        Paging<T> FindList<TKey>(Expression<Func<T, bool>> predicate, Expression<Func<T, TKey>> keySelector, bool isAsc, int pageIndex, int pageSize);

        /// <summary>
        /// 查询[分页]
        /// </summary>
        /// <typeparam name="TKey">排序属性</typeparam>
        /// <param name="predicate">查询条件</param>
        /// <param name="keySelector">排序</param>
        /// <param name="isAsc">是否正序</param>
        /// <param name="pageIndex">当前页</param>
        /// <param name="pageSize">每页记录数</param>
        /// <returns></returns>
        Task<Paging<T>> FindListAsync<TKey>(Expression<Func<T, bool>> predicate, Expression<Func<T, TKey>> keySelector, bool isAsc, int pageIndex, int pageSize);

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="entity">实体</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>是否删除成功[isSave=true时有效]</returns>
        bool Remove(T entity, bool isSave = true);

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="entity">实体</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>是否删除成功[isSave=true时有效]</returns>
        Task<bool> RemoveAsync(T entity, bool isSave = true);

        /// <summary>
        /// 删除[批量]
        /// </summary>
        /// <param name="entities">实体数组</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>成功删除的记录数[isSave=true时有效]</returns>
        int RemoveRange(T[] entities, bool isSave = true);

        /// <summary>
        /// 删除[批量]
        /// </summary>
        /// <param name="entities">实体数组</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>成功删除的记录数[isSave=true时有效]</returns>
        Task<int> RemoveRangeAsync(T[] entities, bool isSave = true);

        /// <summary>
        ///  保存数据
        /// </summary>
        /// <returns>更改的记录数</returns>
        int SaveChanges();

        /// <summary>
        ///  保存数据
        /// </summary>
        /// <returns>更改的记录数</returns>
        Task<int> SaveChangesAsync();

        /// <summary>
        /// 更新
        /// </summary>
        /// <param name="entity">实体</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>是否保存成功[isSave=true时有效]</returns>
        bool Update(T entity, bool isSave = true);

        /// <summary>
        /// 更新
        /// </summary>
        /// <param name="entity">实体</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>是否保存成功[isSave=true时有效]</returns>
        Task<bool> UpdateAsync(T entity, bool isSave = true);

        /// <summary>
        /// 更新[批量]
        /// </summary>
        /// <param name="entities">实体数组</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>更新成功的记录数[isSave=true时有效]</returns>
        int UpdateRange(T[] entities, bool isSave = true);

        /// <summary>
        /// 更新[批量]
        /// </summary>
        /// <param name="entities">实体数组</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>更新成功的记录数[isSave=true时有效]</returns>
        Task<int> UpdateRangeAsync(T[] entities, bool isSave = true);

    }
}

2、异步接口的实现

在Ninesky.Base中打开BaseService 添加异步接口的实现,代码如下

using Microsoft.EntityFrameworkCore;
using Ninesky.InterfaceBase;
using Ninesky.Models;
using System;
using System.Linq;
using System.Linq.Expressions;
using System.Threading.Tasks;

namespace Ninesky.Base
{
    /// <summary>
    /// 服务基类
    /// </summary>
    public class BaseService<T>:InterfaceBaseService<T> where T:class
    {
        protected DbContext _dbContext;
        public BaseService(DbContext dbContext)
        {
            _dbContext = dbContext;
        }

        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="entity">实体</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>添加的记录数[isSave=true时有效]</returns>
        public virtual int Add(T entity, bool isSave = true)
        {
            _dbContext.Set<T>().Add(entity);
            if (isSave) return _dbContext.SaveChanges();
            else return 0;
        }

        /// <summary>
        /// 添加
        /// </summary>
        /// <param name="entity">实体</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>添加的记录数[isSave=true时有效]</returns>
        public virtual async Task<int> AddAsync(T entity, bool isSave = true)
        {
            await _dbContext.Set<T>().AddAsync(entity);
            if (isSave) return await _dbContext.SaveChangesAsync();
            else return 0;
        }

        /// <summary>
        /// 添加[批量]
        /// </summary>
        /// <param name="entities">实体</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>添加的记录数[isSave=true时有效]</returns>
        public virtual int AddRange(T[] entities, bool isSave = true)
        {
            _dbContext.Set<T>().AddRange(entities);
            if (isSave) return _dbContext.SaveChanges();
            else return 0;
        }

        /// <summary>
        /// 添加[批量]
        /// </summary>
        /// <param name="entities">实体</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>添加的记录数[isSave=true时有效]</returns>
        public virtual async Task<int> AddRangeAsync(T[] entities, bool isSave = true)
        {
            await _dbContext.Set<T>().AddRangeAsync(entities);
            if (isSave) return await _dbContext.SaveChangesAsync();
            else return 0;
        }

        /// <summary>
        /// 查询记录数
        /// </summary>
        /// <param name="predicate">查询条件</param>
        /// <returns>记录数</returns>
        public virtual int Count(Expression<Func<T, bool>> predicate)
        {
            return _dbContext.Set<T>().Count(predicate);
        }

        /// <summary>
        /// 查询记录数
        /// </summary>
        /// <param name="predicate">查询条件</param>
        /// <returns>记录数</returns>
        public virtual async Task<int> CountAsync(Expression<Func<T, bool>> predicate)
        {
            return await _dbContext.Set<T>().CountAsync(predicate);
        }

        /// <summary>
        /// 查询是否存在
        /// </summary>
        /// <param name="predicate">查询条件</param>
        /// <returns>是否存在</returns>
        public virtual bool Exists(Expression<Func<T, bool>> predicate)
        {
            return _dbContext.Set<T>().Any(predicate);
        }

        /// <summary>
        /// 查询是否存在
        /// </summary>
        /// <param name="predicate">查询条件</param>
        /// <returns>是否存在</returns>
        public virtual async Task<bool> ExistsAsync(Expression<Func<T, bool>> predicate)
        {
            return await _dbContext.Set<T>().AnyAsync(predicate);
        }

        /// <summary>
        /// 查找
        /// </summary>
        /// <param name="Id">主键</param>
        /// <returns></returns>
        public virtual T Find(int Id)
        {
            return _dbContext.Set<T>().Find(Id);
        }

        /// <summary>
        /// 查找
        /// </summary>
        /// <param name="Id">主键</param>
        /// <returns></returns>
        public virtual async Task<T> FindAsync(int Id)
        {
            return await _dbContext.Set<T>().FindAsync(Id);
        }

        /// <summary>
        /// 查找
        /// </summary>
        /// <param name="keyValues">主键</param>
        /// <returns></returns>
        public virtual T Find(object[] keyValues)
        {
            return _dbContext.Set<T>().Find(keyValues);
        }

        /// <summary>
        /// 查找
        /// </summary>
        /// <param name="keyValues">主键</param>
        /// <returns></returns>
        public virtual async Task<T> FindAsync(object[] keyValues)
        {
            return await _dbContext.Set<T>().FindAsync(keyValues);
        }

        public virtual T Find(Expression<Func<T, bool>> predicate)
        {
            return _dbContext.Set<T>().SingleOrDefault(predicate);
        }

        /// <summary>
        /// 查找
        /// </summary>
        /// <param name="predicate">查询条件</param>
        /// <returns></returns>
        public virtual async Task<T> FindAsync(Expression<Func<T, bool>> predicate)
        {
            return await _dbContext.Set<T>().SingleOrDefaultAsync(predicate);
        }

        /// <summary>
        /// 查询
        /// </summary>
        /// <returns></returns>
        public virtual IQueryable<T> FindList()
        {
            return _dbContext.Set<T>();
        }

        /// <summary>
        /// 查询
        /// </summary>
        /// <returns>实体列表</returns>
        public virtual async Task<IQueryable<T>> FindListAsync()
        {
            IQueryable<T> result = _dbContext.Set<T>();
            return await Task.FromResult(result);
        }

        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="predicate">查询条件</param>
        /// <returns>实体列表</returns>
        public virtual IQueryable<T> FindList(Expression<Func<T, bool>> predicate)
        {
            return _dbContext.Set<T>().Where(predicate);
        }

        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="predicate">查询条件</param>
        /// <returns>实体列表</returns>
        public virtual async Task<IQueryable<T>> FindListAsync(Expression<Func<T, bool>> predicate)
        {
            return await Task.FromResult(FindList(predicate));
        }

        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="number">返回记录数</param>
        /// <param name="predicate">查询条件</param>
        /// <returns>实体列表</returns>
        public virtual IQueryable<T> FindList(int number, Expression<Func<T, bool>> predicate)
        {
            var entityList = _dbContext.Set<T>().Where(predicate);
            if (number > 0) return entityList.Take(number);
            else return entityList;
        }

        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="number">返回记录数</param>
        /// <param name="predicate">查询条件</param>
        /// <returns>实体列表</returns>
        public virtual async Task<IQueryable<T>> FindListAsync(int number, Expression<Func<T, bool>> predicate)
        {
            return await Task.FromResult(FindList(number, predicate));
        }
        
        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="number">显示数量[小于等于0-不启用]</param>
        /// <typeparam name="TKey">排序字段</typeparam>
        /// <param name="predicate">查询条件</param>
        /// <param name="keySelector">排序</param>
        /// <param name="isAsc">正序</param>
        /// <returns></returns>
        public virtual IQueryable<T> FindList<TKey>(int number, Expression<Func<T, bool>> predicate, Expression<Func<T, TKey>> keySelector, bool isAsc)
        {
            var entityList = _dbContext.Set<T>().Where(predicate);
            if (isAsc) entityList = entityList.OrderBy(keySelector);
            else entityList.OrderByDescending(keySelector);
            if (number > 0) return entityList.Take(number);
            else return entityList;
        }

        /// <summary>
        /// 查询
        /// </summary>
        /// <param name="number">显示数量[小于等于0-不启用]</param>
        /// <typeparam name="TKey">排序字段</typeparam>
        /// <param name="predicate">查询条件</param>
        /// <param name="keySelector">排序</param>
        /// <param name="isAsc">正序</param>
        /// <returns></returns>
        public virtual async Task<IQueryable<T>> FindListAsync<TKey>(int number, Expression<Func<T, bool>> predicate, Expression<Func<T, TKey>> keySelector, bool isAsc)
        {
            var entityList = _dbContext.Set<T>().Where(predicate);
            if (isAsc) entityList = entityList.OrderBy(keySelector);
            else entityList.OrderByDescending(keySelector);
            if (number > 0) entityList = entityList.Take(number);
            return await Task.FromResult(entityList);
        }

        /// <summary>
        /// 查询[分页]
        /// </summary>
        /// <typeparam name="TKey">排序属性</typeparam>
        /// <param name="predicate">查询条件</param>
        /// <param name="keySelector">排序</param>
        /// <param name="isAsc">是否正序</param>
        /// <param name="paging">分页数据</param>
        /// <returns></returns>
        public virtual Paging<T> FindList<TKey>(Expression<Func<T, bool>> predicate, Expression<Func<T, TKey>> keySelector, bool isAsc, Paging<T> paging)
        {
            var entityList = _dbContext.Set<T>().Where(predicate);
            paging.Total = entityList.Count();
            if (isAsc) entityList = entityList.OrderBy(keySelector);
            else entityList.OrderByDescending(keySelector);
            paging.Entities = entityList.Skip((paging.PageIndex - 1) * paging.PageSize).Take(paging.PageSize).ToList();
            return paging;
        }

        /// <summary>
        /// 查询[分页]
        /// </summary>
        /// <typeparam name="TKey">排序属性</typeparam>
        /// <param name="predicate">查询条件</param>
        /// <param name="keySelector">排序</param>
        /// <param name="isAsc">是否正序</param>
        /// <param name="paging">分页数据</param>
        /// <returns></returns>
        public virtual async Task<Paging<T>> FindListAsync<TKey>(Expression<Func<T, bool>> predicate, Expression<Func<T, TKey>> keySelector, bool isAsc, Paging<T> paging)
        {
            var entityList = _dbContext.Set<T>().Where(predicate);
            paging.Total = await entityList.CountAsync();
            if (isAsc) entityList = entityList.OrderBy(keySelector);
            else entityList.OrderByDescending(keySelector);
            paging.Entities = await entityList.Skip((paging.PageIndex - 1) * paging.PageSize).Take(paging.PageSize).ToListAsync();
            return paging;
        }

        /// <summary>
        /// 查询[分页]
        /// </summary>
        /// <typeparam name="TKey">排序属性</typeparam>
        /// <param name="predicate">查询条件</param>
        /// <param name="keySelector">排序</param>
        /// <param name="isAsc">是否正序</param>
        /// <param name="pageIndex">当前页</param>
        /// <param name="pageSize">每页记录数</param>
        /// <returns></returns>
        public virtual Paging<T> FindList<TKey>(Expression<Func<T, bool>> predicate, Expression<Func<T, TKey>> keySelector, bool isAsc, int pageIndex, int pageSize)
        {
            Paging<T> paging = new Paging<T> { PageIndex = pageIndex, PageSize = pageSize };
            return FindList(predicate, keySelector, isAsc, paging);
        }

        /// <summary>
        /// 查询[分页]
        /// </summary>
        /// <typeparam name="TKey">排序属性</typeparam>
        /// <param name="predicate">查询条件</param>
        /// <param name="keySelector">排序</param>
        /// <param name="isAsc">是否正序</param>
        /// <param name="pageIndex">当前页</param>
        /// <param name="pageSize">每页记录数</param>
        /// <returns></returns>
        public virtual async Task<Paging<T>> FindListAsync<TKey>(Expression<Func<T, bool>> predicate, Expression<Func<T, TKey>> keySelector, bool isAsc, int pageIndex, int pageSize)
        {
            Paging<T> paging = new Paging<T> { PageIndex = pageIndex, PageSize = pageSize };
            return await FindListAsync(predicate, keySelector, isAsc, paging);
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="entity">实体</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>是否删除成功[isSave=true时有效]</returns>
        public virtual bool Remove(T entity, bool isSave = true)
        {
            _dbContext.Set<T>().Remove(entity);
            if (isSave) return _dbContext.SaveChanges() > 0;
            else return false;
        }

        /// <summary>
        /// 删除
        /// </summary>
        /// <param name="entity">实体</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>是否删除成功[isSave=true时有效]</returns>
        public virtual async Task<bool> RemoveAsync(T entity, bool isSave = true)
        {
            _dbContext.Set<T>().Remove(entity);
            if (isSave) return await _dbContext.SaveChangesAsync() > 0;
            else return false;
        }


        /// <summary>
        /// 删除[批量]
        /// </summary>
        /// <param name="entities">实体数组</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>成功删除的记录数</returns>
        public virtual int RemoveRange(T[] entities, bool isSave = true)
        {
            _dbContext.Set<T>().RemoveRange(entities);
            if (isSave) return _dbContext.SaveChanges();
            else return 0;
        }

        /// <summary>
        /// 删除[批量]
        /// </summary>
        /// <param name="entities">实体数组</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>成功删除的记录数[isSave=true时有效]</returns>
        public virtual async Task<int> RemoveRangeAsync(T[] entities, bool isSave = true)
        {
            _dbContext.Set<T>().RemoveRange(entities);
            if (isSave) return await _dbContext.SaveChangesAsync();
            else return 0;
        }
        
        /// <summary>
        ///  保存数据
        /// </summary>
        /// <returns>更改的记录数</returns>
        public virtual int SaveChanges()
        {
            return _dbContext.SaveChanges();
        }

        /// <summary>
        ///  保存数据
        /// </summary>
        /// <returns>更改的记录数</returns>
        public virtual async Task<int> SaveChangesAsync()
        {
            return await _dbContext.SaveChangesAsync();
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <param name="entity">实体</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>是否保存成功</returns>
        public virtual bool Update(T entity, bool isSave = true)
        {
            _dbContext.Set<T>().Update(entity);
            if (isSave) return _dbContext.SaveChanges() > 0;
            else return false;
        }

        /// <summary>
        /// 更新
        /// </summary>
        /// <param name="entity">实体</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>是否保存成功[isSave=true时有效]</returns>
        public async Task<bool> UpdateAsync(T entity, bool isSave = true)
        {
            _dbContext.Set<T>().Update(entity);
            if (isSave) return await _dbContext.SaveChangesAsync() > 0;
            else return false;
        }

        /// <summary>
        /// 更新[批量]
        /// </summary>
        /// <param name="entities">实体数组</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>更新成功的记录数</returns>
        public virtual int UpdateRange(T[] entities, bool isSave = true)
        {
            _dbContext.Set<T>().UpdateRange(entities);
            if (isSave) return _dbContext.SaveChanges();
            else return 0;
        }

        /// <summary>
        /// 更新[批量]
        /// </summary>
        /// <param name="entities">实体数组</param>
        /// <param name="isSave">是否立即保存</param>
        /// <returns>更新成功的记录数[isSave=true时有效]</returns>
        public virtual async Task<int> UpdateRangeAsync(T[] entities, bool isSave = true)
        {
            _dbContext.Set<T>().UpdateRange(entities);
            if (isSave) return await _dbContext.SaveChangesAsync();
            else return 0;
        }
    }
}

二、模块功能

按照设想模块应该可以包含文章模块、咨询模块、产品模块、图片模块等,这里先实现文章模块。由于模块功能是系统预先写好的功能,所以模块不用添加、修改、删除等功能,只需要显示和启用(禁用)就好了。

1、添加模块模型

在Ninesky.Models中添加Module类

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

namespace Ninesky.Models
{
    /// <summary>
    /// 模块模型
    /// </summary>
    public class Module
    {
        [Key]
        public int ModuleId { get; set; }

        /// <summary>
        /// 模块名称
        /// </summary>
        [Required(ErrorMessage = "{0}必填")]
        [StringLength(50)]
        [Display(Name = "模块名称")]
        public string Name { get; set; }

        /// <summary>
        /// 模块控制器
        /// </summary>
        [StringLength(50)]
        [Display(Name = "模块控制器")]
        public string Controller { get; set; }

        /// <summary>
        /// 模块说明
        /// </summary>
        [DataType(DataType.MultilineText)]
        [StringLength(1000)]
        [Display(Name = "模块说明")]
        public string Description { get; set; }

        /// <summary>
        /// 是否启用
        /// </summary>
        [Required(ErrorMessage = "{0}必填")]
        [Display(Name = "启用")]
        public bool Enabled { get; set; }

        /// <summary>
        /// 排序
        /// </summary>
        public virtual List<ModuleOrder> ModuleOrders { get; set; }
    }
}

类有个导航属性ModuleOrders类型是ModuleOrder的列表。这个表示模块支持的排序类型,在添加栏目的时候选择排序类型,在前台栏目显示内容列表的时候按功能进行排序。代码如下:

using System.ComponentModel.DataAnnotations;


namespace Ninesky.Models
{
    /// <summary>
    /// 模块排序类型
    /// </summary>
    public class ModuleOrder
    {
        [Key]
        public int ModuleOrderId { get; set; }

        /// <summary>
        /// 模块ID
        /// </summary>
        [Required]
        [Display(Name = "模块ID")]
        public int ModuleId { get; set; }

        /// <summary>
        /// 名称
        /// </summary>
        [Required]
        [StringLength(50)]
        [Display(Name = "名称")]
        public string Name { get; set; }

        /// <summary>
        ////// </summary>
        [Required]
        [Display(Name = "")]
        public int Order { get; set; }

        /// <summary>
        /// 模块
        /// </summary>
        public virtual Module Module { get; set; }
    }
}

2、模块接口

在Ninesky.InterfaceBase添加模块的接口Ninesky.InterfaceBase,继承自InterfaceBaseService<Module>,并添加两个新的方法。查找模块列表和查找模块支持的排序列表

using Ninesky.Models;
using System.Linq;
using System.Threading.Tasks;

namespace Ninesky.InterfaceBase
{
    public interface InterfaceModuleService:InterfaceBaseService<Module>
    {
        /// <summary>
        /// 查找
        /// </summary>
        /// <param name="enable">启用</param>
        /// <returns></returns>
        Task<IQueryable<Module>> FindListAsync(bool? enable);
        /// <summary>
        /// 查找排序列表
        /// </summary>
        /// <param name="moduleId">模块ID</param>
        /// <returns></returns>
        Task<IQueryable<ModuleOrder>> FindOrderListAsync(int moduleId);
    }
}

3、在Ninesky.Base中添加类ModuleService来实现模块接口

using Microsoft.EntityFrameworkCore;
using Ninesky.InterfaceBase;
using Ninesky.Models;
using System.Linq;
using System.Threading.Tasks;

namespace Ninesky.Base
{
    public class ModuleService:BaseService<Module>,InterfaceModuleService
    {
        public ModuleService(DbContext dbContext) : base(dbContext)
        { }

        public override Module Find(int Id)
        {
            return _dbContext.Set<Module>().Include(m => m.ModuleOrders).SingleOrDefault(m => m.ModuleId == Id);
        }

        /// <summary>
        /// 查找
        /// </summary>
        /// <param name="enable">启用</param>
        /// <returns></returns>
        public async Task<IQueryable<Module>> FindListAsync(bool? enable)
        {
            if (enable == null) return await FindListAsync();
            else return await FindListAsync(m => m.Enabled == enable);
        }
        /// <summary>
        /// 查找排序列表
        /// </summary>
        /// <param name="moduleId">模块ID</param>
        /// <returns></returns>
        public async Task<IQueryable<ModuleOrder>> FindOrderListAsync(int moduleId)
        {
            return await Task.FromResult(_dbContext.Set<ModuleOrder>().Where(mo => mo.ModuleId == moduleId));
        }
    }
}

4、模块控制器

在Ninesky.Web.Areas.System.Controllers中添加模块控制器ModuleController,控制器有一个几个功能

  • Index,首页,返回页面视图,在视图冲通过ajax方式加载模块列表
  • List,模块列表,返回json类型数据,方便其他视图调用。
  • Details,详细视图,包含功能信息和排序列表,可以在次视图中启用或禁用模块
  • Enable,启用或禁用模块,此Action没有视图,通过post提交数据,返回json类型的操作状态数据
  • OrderList,模块排序列表,返回json类型数据。

Enable方法返回操作结果时,考虑到不光要返回是否操作成功,可能还要返回消息字符串,所以专门写个类型统一返回ajax操作的结果。把类型命名为JsonResponse,放到Ninesky.Web.Models下,代码如下图:

namespace Ninesky.Web.Models
{
    /// <summary>
    /// 返回Json数据类型
    /// </summary>
    public class JsonResponse
    {
        /// <summary>
        /// 操作是否成功
        /// </summary>
        public bool succeed { get; set; }

        /// <summary>
        /// 操作结果详细代码【必要时】
        /// </summary>
        public int code { get; set; }

        /// <summary>
        /// 操作结果消息
        /// </summary>
        public string message { get; set; }

        /// <summary>
        /// 操作产生的数据【必要时】
        /// </summary>
        public dynamic Data { get; set; }

        public JsonResponse()
        {
            succeed = false;
            message = "未知错误";
        }
    }
}

整个控制器代码如下:

using Microsoft.AspNetCore.Mvc;
using Ninesky.InterfaceBase;
using Ninesky.Web.Models;
using System.Linq;
using System.Threading.Tasks;

namespace Ninesky.Web.Areas.System.Controllers
{
    [Area("System")]
    public class ModuleController : Controller
    {
        private InterfaceModuleService _moduleService;

        public ModuleController(InterfaceModuleService moduleService)
        {
            _moduleService = moduleService;
        }

        /// <summary>
        /// 详细
        /// </summary>
        /// <param name="id">模块ID</param>
        /// <returns></returns>
        public async Task<IActionResult> Details(int id)
        {
            return View(await _moduleService.FindAsync(id));
        }

        [HttpPost]
        public async Task<IActionResult> Enable (int id, bool enabled)
        {
            JsonResponse jsonResponse = new JsonResponse();
            var module = await _moduleService.FindAsync(id);
            if(module == null)
            {
                jsonResponse.succeed = false;
                jsonResponse.message = "模块不存在";
            }
            else
            {
                module.Enabled = enabled;
                if(await _moduleService.UpdateAsync(module))
                {
                    jsonResponse.succeed = true;
                    jsonResponse.message = "模块已" + (enabled ? "启用" : "禁用");
                }
                else
                {
                    jsonResponse.succeed = false;
                    jsonResponse.message = "保存数据失败";
                }
            }
            return Json(jsonResponse);
        }

        public IActionResult Index()
        {
            return View();
        }
        /// <summary>
        /// 模块列表
        /// </summary>
        /// <returns></returns>
        public async Task<IActionResult> List()
        {
            return Json((await _moduleService.FindListAsync()).ToList());
        }

        /// <summary>
        /// 排序列表
        /// </summary>
        /// <param name="id">模块Id</param>
        /// <returns></returns>
        public async Task<IActionResult> OrderList(int id)
        {
            return Json((await _moduleService.FindOrderListAsync(id)).ToList());
        }

    }
}

5、视图

在控制器代码中可以看出只有两个action返回了视图,一个是Index,另一个是Details。

模块首页视图

在控制器中index没有像视图提供数据,在视图中通过ajax方式加载。在视图中使用bootstrapTable组件来显示视图列表。改组件可以在项目的依赖项->Bower【右键】->管理Bower程序包中搜索bootstrap-Table,并安装。

image

视图代码:

@{
    ViewData["Title"] = "模块管理";
}

<ol class="breadcrumb">
    <li><span class="fa fa-home"></span>  <a asp-controller="Home" asp-action="Index">首页</a></li>
    <li><a asp-controller="System" asp-action="Index">系统配置</a></li>
    <li class="active">模块管理</li>
</ol>
<table id="moduletable"></table>

@section aside{

    @Html.Partial("SystemAside")
}
@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
    <script type="text/javascript">
        $(document).ready(function () {
            $('#moduletable').bootstrapTable({
                url: '@Url.Action("List")',
                columns: [{
                    field: 'moduleId',
                    title: '序号'
                }, {
                    field: 'name',
                    title: '名称',
                    formatter: function (value, row, index) {
                        return '<a href="@Url.Action("Details")/' + row.moduleId + '">' + value + '</a>';
                    }
                }, {
                    field: 'controller',
                    title: '控制器'
                }, {
                    field: 'description',
                    title: '说明'
                }, {
                    field: 'enabled',
                    title: '状态',
                    formatter:function(value,row,index)
                    {
                        return value ? '<i class="fa fa-check" aria-hidden="true"></i>' : '<i class="fa fa-ban" aria-hidden="true"></i>';
                    }
                }]
            });
        });
    </script>
}

完成的效果图

image

详细信息视图

Details中使用了tabs,一个标签显示基本信息,另一个标签显示排序列表。基本信息的启用属性使用checkbox,点击可以启用/禁用模块。排序列表继续使用bootstrapTable组件显示列表。

@model Ninesky.Models.Module
@{
    ViewData["Title"] = Model.Name;
}

<ol class="breadcrumb">
    <li><span class="fa fa-home"></span>  <a asp-controller="Home" asp-action="Index">首页</a></li>
    <li><a asp-controller="System" asp-action="Index">系统配置</a></li>
    <li><a asp-controller="Module" asp-action="Index">模块管理</a></li>
    <li class="active">@Model.Name</li>
</ol>


@section aside{

    @Html.Partial("SystemAside")
}
<!-- Nav tabs -->
<ul class="nav nav-tabs" role="tablist">
    <li role="presentation" class="active"><a href="#base" role="tab" data-toggle="tab">基本信息</a></li>
    <li role="presentation"><a href="#order" role="tab" data-toggle="tab">排序方式</a></li>
</ul>
<!-- Tab panes -->
<div class="tab-content">
    <div role="tabpanel" class="tab-pane active" id="base">
        <input type="hidden" asp-for="ModuleId" />
        <dl class="dl-horizontal">
            <dt>
                <label asp-for="Name"></label>
            </dt>
            <dd>
                @Html.DisplayFor(model => model.Name)
            </dd>
            <dt>
                <label asp-for="Controller"></label>
            </dt>
            <dd>
                @Html.DisplayFor(model => model.Controller)
            </dd>
            <dt>
                <label asp-for="Description"></label>
            </dt>
            <dd>
                @Html.DisplayFor(model => model.Description)
            </dd>
            <dt>
                <label asp-for="Enabled"></label>
            </dt>
            <dd>
                <input type="checkbox" asp-for="Enabled" />
            </dd>
        </dl>
    </div>
    <div role="tabpanel" class="tab-pane" id="order"><table id="moduleordertable"></table></div>
</div>
@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
    <script type="text/javascript">
        $(document).ready(function () {
            $('#moduleordertable').bootstrapTable({
                url: '@Url.Action("OrderList",new{
            id=Model.ModuleId})',
                columns: [{
                    field: 'moduleOrderId',
                    title: '序号'
                }, {
                    field: 'name',
                    title: '名称',
                }, {
                    field: 'order',
                    title: ''
                }]
            });
            $('#Enabled').click(function () {
                $.post('@Url.Action("Enable", "Module")', { id: $('#ModuleId').val(), enabled: $('#Enabled').prop('checked') }, function (response) { 
                    if(response.succeed)
                    {
                        BootstrapDialog.alert({
                            title:'消息',
                            message: response.message,
                            buttonLabel: '确定'
                        });

                    }
                }, 'json');
            });
        });
    </script>
}

界面显示如下:

image

 

三、栏目

1、栏目接口

通常会用树形菜单的形式显示栏目结构,需要添加栏目的树形菜单数据,需要添加一个方法,另外显示子栏目也需要添加一个方法。

在栏目接口中添加方法。

using Ninesky.Models;
using System.Linq;
using System.Threading.Tasks;

namespace Ninesky.InterfaceBase
{
    /// <summary>
    /// 栏目服务接口
    /// </summary>
    public interface InterfaceCategoryService:InterfaceBaseService<Category>
    {
        /// <summary>
        /// 查找树形菜单
        /// </summary>
        /// <param name="categoryType">栏目类型,可以为空</param>
        /// <returns></returns>
        Task<IQueryable<Category>> FindTreeAsync(CategoryType? categoryType);

        /// <summary>
        /// 查找子栏目
        /// </summary>
        /// <param name="id">栏目ID</param>
        /// <returns></returns>
        IQueryable<Category> FindChildren(int id);

        /// <summary>
        /// 查找子栏目
        /// </summary>
        /// <param name="id">栏目ID</param>
        /// <returns></returns>
        Task<IQueryable<Category>> FindChildrenAsync(int id);
    }
}

2、接口的实现。

  • 在基类中的方法只返回栏目实体并没有返回外键,所以实现类中需要重写查找栏目的方法将三种栏目类型的导航属性都加载进来。
  • 查找子栏目的方法直接返回结果就行。
  • 查找栏目树稍微麻烦点,在添加栏目的时候需要选择父栏目,父栏目的类型只能是常规栏目,所以方法要有栏目类型参数:在参数为null时返回所有栏目列表;栏目不为空时,先在数据库中查找所有此类型的栏目,然后根据ParentPath来取出所有的父栏目(重复的剔除)。计划在前台使用ztree组件显示树,该组件有个简单数据类型,根据pid自动生成树,这里就不用在组织成树形接口直接返回列表就可以了。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Microsoft.EntityFrameworkCore;
using Ninesky.Models;
using Ninesky.InterfaceBase;

namespace Ninesky.Base
{
    /// <summary>
    /// 栏目服务类
    /// </summary>
    public class CategoryService:BaseService<Category>,InterfaceCategoryService
    {
        public CategoryService(DbContext dbContext):base(dbContext)
        {
        }
        /// <summary>
        /// 查找
        /// </summary>
        /// <param name="Id">栏目ID</param>
        /// <returns></returns>
        public override Category Find(int Id)
        {
            return _dbContext.Set<Category>().Include("General").Include("Page").Include("Link").SingleOrDefault(c => c.CategoryId == Id);
        }

        /// <summary>
        /// 查找子栏目
        /// </summary>
        /// <param name="id">栏目ID</param>
        /// <returns></returns>
        public IQueryable<Category> FindChildren(int id)
        {
            return FindList(0, c => c.ParentId == id, c => c.Order, true);
        }

        /// <summary>
        /// 查找子栏目
        /// </summary>
        /// <param name="id">栏目ID</param>
        /// <returns></returns>
        public async Task<IQueryable<Category>> FindChildrenAsync(int id)
        {
            return await FindListAsync(0, c => c.ParentId == id, c => c.Order, true);
        }

        /// <summary>
        /// 查找树形菜单
        /// </summary>
        /// <param name="categoryType">栏目类型,可以为空</param>
        /// <returns></returns>
        public async Task<IQueryable<Category>> FindTreeAsync(CategoryType? categoryType)
        {
            var categories = await FindListAsync();
            //根据栏目类型分类处理
            switch (categoryType)
            {
                case null:
                    break;
                case CategoryType.General:
                    categories = categories.Where(c => c.Type == categoryType);
                    break;
                    //默认-Page或Link类型
                default:
                    //Id数组-含本栏目及父栏目
                    List<int> idArray = new List<int>();
                    //查找栏目id及父栏目路径
                    var categoryArray = categories.Where(c => c.Type == categoryType).Select(c => new { CategoryId = c.CategoryId, ParentPath = c.ParentPath });
                    if(categoryArray != null)
                    {
                        //添加栏目ID到
                        idArray.AddRange(categoryArray.Select(c => c.CategoryId));
                        foreach (var parentPath in categoryArray.Select(c=>c.ParentPath))
                        {
                            var parentIdArray = parentPath.Split(new char[] { ',' }, StringSplitOptions.RemoveEmptyEntries);
                            if (parentIdArray != null)
                            {
                                int parseId = 0;
                                foreach(var parentId in parentIdArray)
                                {
                                    if (int.TryParse(parentId, out parseId)) idArray.Add(parseId);
                                }
                            }
                        }
                    }
                    categories = categories.Where(c => idArray.Contains(c.CategoryId));
                    break;
            }
            return categories.OrderBy(c => c.ParentPath).ThenBy(C => C.Order);
        }
    }
}

3、栏目控制器。

栏目控制器有一下几个action

  • Index,首页,返回页面视图
  • Add,添加栏目,一个有两个action,一个用于显示,另一个接受post过来的数据进行处理。
  • Tree,全部栏目数据,返回json类型数据用于显示侧栏显示。
  • ParentTree,常规栏目数据,返回json类型的常规栏目及其父栏目数据,用于选择父栏目时控件。

在显示树形菜单时使用的ztree组件,是一个功能非常强大的国产组件,需要的朋友可以去看看http://www.treejs.cn/v3/api.php

控制器中只有Add方法麻烦一点,添加时需要对其父栏目和导航属性进行判断和处理。代码如下:

using Microsoft.AspNetCore.Mvc;
using Microsoft.AspNetCore.Mvc.Rendering;
using Ninesky.InterfaceBase;
using Ninesky.Models;
using Ninesky.Web.Models;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;

namespace Ninesky.Web.Areas.System.Controllers
{
    /// <summary>
    /// 栏目控制器
    /// </summary>
    [Area("System")]
    public class CategoryController : Controller
    {
        private InterfaceCategoryService _categoryService;
        public CategoryController(InterfaceCategoryService categoryService)
        {
            _categoryService = categoryService;
        }

        public async Task<IActionResult> Add([FromServices]InterfaceModuleService moduleService, CategoryType? categoryType)
        {

            var modules = await moduleService.FindListAsync(true);
            var modeleArry = modules.Select(m => new SelectListItem { Text = m.Name, Value = m.ModuleId.ToString() }).ToList();
            modeleArry.Insert(0, new SelectListItem() { Text = "", Value = "0", Selected = true });
            ViewData["Modules"] = modeleArry;
            return View(new Category() { Type = CategoryType.General, ParentId = 0, View="Index", Order = 0, Target = LinkTarget._self, General = new CategoryGeneral() { ContentView = "Index" } });
        }

        [HttpPost]
        public async Task<IActionResult>  Add([FromServices]InterfaceModuleService moduleService,Category category)
        {
            if(ModelState.IsValid)
            {
                //检查父栏目
                if (category.ParentId > 0)
                {
                    var parentCategory = await _categoryService.FindAsync(category.ParentId);
                    if (parentCategory == null) ModelState.AddModelError("ParentId", "父栏目不存在");
                    else if (parentCategory.Type != CategoryType.General) ModelState.AddModelError("ParentId", "父栏目不能添加子栏目");
                    else category.ParentPath = parentCategory.ParentPath + "," + parentCategory.CategoryId;
                }
                else category.ParentPath = "0";
                //检查栏目类型
                switch (category.Type)
                {
                    case CategoryType.General:
                        if (category.General == null) ModelState.AddModelError("General.Type", "请填写常规栏目内容");
                        else
                        {
                            if (category.General.ModuleId > 0)
                            {
                                if (string.IsNullOrEmpty(category.General.ContentView)) ModelState.AddModelError("General.ContentView", "请填写栏目视图");
                                if (category.General.ContentOrder == null) ModelState.AddModelError("General.ContentOrder", "请选择内容排序方式");
                            }
                            else
                            {
                                if (category.Page != null) category.Page = null;
                                if (category.Link != null) category.Link = null;
                            }
                        }
                        break;
                    case CategoryType.Page:
                        //检查
                        if (category.Page == null) ModelState.AddModelError("General.Type", "请填写单页栏目内容");
                        else
                        {
                            if (string.IsNullOrEmpty(category.Page.Content)) ModelState.AddModelError("Page.Content", "请输入单页栏目内容");
                            else
                            {
                                if (category.General != null) category.General = null;
                                if (category.Link != null) category.Link = null;
                            }
                        }
                        break;
                    case CategoryType.Link:
                        //检查
                        if (category.Link == null) ModelState.AddModelError("General.Type", "请填写连接栏目内容");
                        else
                        {
                            if (string.IsNullOrEmpty(category.Link.Url)) ModelState.AddModelError("Link.Url", "请选择输入链接地址");
                            else
                            {
                                if (category.General != null) category.General = null;
                                if (category.General != null) category.General = null;
                            }
                        }
                        break;
                }
                
                //保存到数据库
                if(ModelState.IsValid)
                {
                    if (await _categoryService.AddAsync(category) > 0) return View("AddSucceed", category);
                    else ModelState.AddModelError("", "保存数据失败");
                }
            }
            var modules = await moduleService.FindListAsync(true);
            var modeleArry = modules.Select(m => new SelectListItem { Text = m.Name, Value = m.ModuleId.ToString() }).ToList();
            modeleArry.Insert(0, new SelectListItem() { Text = "", Value = "0", Selected = true });
            ViewData["Modules"] = modeleArry;
            return View(category);
        }

        /// <summary>
        /// 栏目首页
        /// </summary>
        /// <returns></returns>
        public IActionResult Index()
        {
            return View("Index");
        }

        /// <summary>
        /// 父栏目树
        /// </summary>
        /// <returns></returns>
        public async Task<IActionResult> ParentTree()
        {
            var categories = await _categoryService.FindTreeAsync(CategoryType.General);
            return Json(categories.Select(c => new zTreeNode { id = c.CategoryId, name = c.Name, pId = c.ParentId, iconSkin="fa fa-folder" }));
        }

        /// <summary>
        /// 栏目树
        /// </summary>
        /// <returns></returns>
        public async Task<IActionResult> Tree()
        {
            List<zTreeNode> nodes;
            var categories = await _categoryService.FindTreeAsync(null);
            if (categories != null)
            {
                nodes = new List<zTreeNode>(categories.Count());
                foreach(var category in categories)
                {
                    var node = new zTreeNode() { id = category.CategoryId, pId= category.ParentId, name = category.Name, url = Url.Action("Details", "Category", new { id = category.CategoryId }) };
                    switch(category.Type)
                    {
                        case CategoryType.General:
                            node.iconSkin = "fa fa-folder";
                            node.iconOpen = "fa fa-folder-open";
                            node.iconClose = "fa fa-folder";
                            break;
                        case CategoryType.Page:
                            node.iconSkin = "fa fa-file";
                            break;
                        case CategoryType.Link:
                            node.iconSkin = "fa fa-link";
                            break;
                    }
                    nodes.Add(node);
                }
            }
            else nodes = new List<zTreeNode>();
            return Json(nodes);
        }
    }
}
 

4、视图

左侧导航栏视图

视图名Aside,视图中采用ztree加载栏目树

<div class="panel panel-default">
    <div class="panel-heading">
        <h5 class="panel-title"><span class="fa fa-list"></span> 栏目列表</h5>
    </div>
    <div class="panel-body">
        <ul id="categoryTree" data-url="@Url.Action("Tree", "Category")" class="ztree"></ul>
    </div>
</div>

添加栏目视图

html、js都混在一起了代码很乱,凑活看吧。

@model Ninesky.Models.Category
@{
    ViewData["Title"] = "添加栏目";
}

<ol class="breadcrumb">
    <li><span class="fa fa-home"></span>  <a asp-controller="Home" asp-action="Index">首页</a></li>
    <li><a asp-controller="Category" asp-action="Index">栏目管理</a></li>
    <li class="active">添加常规栏目</li>
</ol>

<div class="panel panel-default">
    <div class="panel-body">
        <form asp-action="Add">
            <div class="form-horizontal">
                <div asp-validation-summary="All" class="text-danger"></div>
                <ul class="nav nav-tabs" role="tablist">
                    <li role="presentation" class="active"><a href="#base" role="tab" data-toggle="tab">基本信息</a></li>
                    <li role="presentation"><a href="#general" role="tab" data-toggle="tab">常规栏目</a></li>
                    <li role="presentation"><a href="#page" role="tab" data-toggle="tab">单页栏目</a></li>
                    <li role="presentation"><a href="#link" role="tab" data-toggle="tab">链接栏目</a></li>
                </ul>

                <!-- Tab panes -->
                <div class="tab-content">
                    <div role="tabpanel" class="tab-pane active" id="base">
                        <div class="form-group">
                            <label asp-for="ParentId" class="col-md-2 control-label"></label>
                            <div class="col-md-10">
                                <div class="input-group" style="width:280px;">
                                    <input id="ParentId-text" type="text" class="form-control" readonly value="无" />
                                    <ul id="ParentId-dropdown" class="dropdown-menu dropdown-menu-left ztree"></ul>
                                    <div class="input-group-btn">
                                        <button id="ParentId-btn" type="button" class="btn btn-default"><span class="caret"></span></button>
                                    </div>
                                    <input asp-for="ParentId" class="form-control" style="width:0; visibility:hidden; position:absolute;padding:0" />
                                </div>
                                <span asp-validation-for="ParentId" class="text-danger"></span>
                            </div>
                        </div>
                        <div class="form-group">
                            <label asp-for="Type" class="col-md-2 control-label"></label>
                            <div class="col-md-10">
                                <select asp-for="Type" asp-items="Html.GetEnumSelectList<Ninesky.Models.CategoryType>()" class="selectpicker form-control" data-style="btn-dropdown"></select>
                                <span asp-validation-for="Type" class="text-danger"></span>
                            </div>
                        </div>
                        <div class="form-group">
                            <label asp-for="Name" class="col-md-2 control-label"></label>
                            <div class="col-md-10">
                                <input asp-for="Name" class="form-control" />
                                <span asp-validation-for="Name" class="text-danger"></span>
                            </div>
                        </div>
                        <div class="form-group">
                            <label asp-for="View" class="col-md-2 control-label"></label>
                            <div class="col-md-10">
                                <input asp-for="View" class="form-control" />
                                <span asp-validation-for="View" class="text-danger"></span>
                            </div>
                        </div>
                        <div class="form-group">
                            <label asp-for="Order" class="col-md-2 control-label"></label>
                            <div class="col-md-10">
                                <input asp-for="Order" class="form-control" />
                                <span asp-validation-for="Order" class="text-danger"></span>
                            </div>
                        </div>
                        <div class="form-group">
                            <label asp-for="Target" class="col-md-2 control-label"></label>
                            <div class="col-md-10">
                                <select asp-for="Target" asp-items="Html.GetEnumSelectList<Ninesky.Models.LinkTarget>()" class="selectpicker form-control" data-style="btn-dropdown"></select>
                                <span asp-validation-for="Target" class="text-danger"></span>
                            </div>
                        </div>
                        <div class="form-group">
                            <label asp-for="Description" class="col-md-2 control-label"></label>
                            <div class="col-md-10">
                                <textarea asp-for="Description" class="form-control"></textarea>
                                <span asp-validation-for="Description" class="text-danger"></span>
                            </div>
                        </div>

                    </div>
                    <div role="tabpanel" class="tab-pane" id="general">
                        <div class="form-group">
                            <label asp-for="General.ModuleId" class="col-md-2 control-label"></label>
                            <div class="col-md-10">
                                <select asp-for="General.ModuleId" asp-items="@ViewData["Modules"] as List<SelectListItem>" class="selectpicker form-control" data-style="btn-dropdown"></select>
                                <span asp-validation-for="General.ModuleId" class="text-danger"></span>
                            </div>
                        </div>
                        <div class="form-group">
                            <label asp-for="General.ContentView" class="col-md-2 control-label"></label>
                            <div class="col-md-10">
                                <input asp-for="General.ContentView" class="form-control" />
                                <span asp-validation-for="General.ContentView" class="text-danger"></span>
                            </div>
                        </div>
                        <div class="form-group">
                            <label asp-for="General.ContentOrder" class="col-md-2 control-label"></label>
                            <div class="col-md-10">
                                <select asp-for="General.ContentOrder" class="form-control"></select>
                                <span asp-validation-for="General.ContentOrder" class="text-danger"></span>
                            </div>
                        </div>
                    </div>
                    <div role="tabpanel" class="tab-pane" id="page">
                        <div class="form-group">
                            <label asp-for="Page.Content" class="control-label"></label>
                            
                                <textarea asp-for="Page.Content" ></textarea>
                                <span asp-validation-for="Page.Content" class="text-danger"></span>
                            
                        </div>
                    </div>
                    <div role="tabpanel" class="tab-pane" id="link">
                        <div class="form-group">
                            <label asp-for="Link.Url" class="col-md-2 control-label"></label>
                            <div class="col-md-10">
                                <input asp-for="Link.Url" class="form-control" />
                                <span asp-validation-for="Link.Url" class="text-danger"></span>
                            </div>
                        </div>
                    </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>
        </form>
    </div>
</div>
@section aside{

    @Html.Partial("Aside")
}
@section Scripts {
    @{await Html.RenderPartialAsync("_ValidationScriptsPartial");}
    <script src="~/lib/ueditor/ueditor.config.js"></script>
    <script src="~/lib/ueditor/ueditor.all.min.js"></script>
    <script type="text/javascript">
        var dropdownCategoryTree;
        var setting = {
            data: {
                simpleData: {
                    enable: true,
                    idKey: "id",
                    pIdKey: "pId",
                    rootPId: 0
                }
            },
            async: {
                enable: true,
                url: "@Url.Action("ParentTree", "Category")"
            },
            callback: {
                onClick: function (event, treeId, treeNode) {
                    $("#ParentId").val(treeNode.id);
                    $("#ParentId-text").val(treeNode.name);
                    $("#ParentId-dropdown").hide();
                }
            }
        };
        function toggleContent() {
            if ($("#General_ModuleId").selectpicker('val') == "") {
                $("#General_ContentOrder").empty();
            }
            else {
                $.post("@Url.Action("OrderList","Module")", { id: $('#General_ModuleId').val() }, function (data) {
                    if (data != undefined) {
                        $.each(data, function (ndex, element) {
                            $("#General_ContentOrder").append("<option value='" + element.order + "'>" + element.name + "</option>");
                        })
                    }
                }, 'json');

            }
        }
        // zTree 的参数配置,深入使用请参考 API 文档(setting 配置详解)
        $(document).ready(function () {
            dropdownCategoryTree = $.fn.zTree.init($("#ParentId-dropdown"), setting);
            dropdownCategoryTree.addNodes(null, { id: 0, name: "" });
            $("#ParentId-text").click(function () {
                $("#ParentId-dropdown").show();
            });
            $("#ParentId-btn").click(function () {
                $("#ParentId-dropdown").show();
            });
            toggleContent();
            $('#General_ModuleId').on('changed.bs.select', function (e) {
                toggleContent();
            });
            //富文本编辑器
            var ue = UE.getEditor('Page_Content');
        });
    </script>
}

添加成功的视图

@model Ninesky.Models.Category
@{
    ViewData["Title"] = "添加栏目";
}

<ol class="breadcrumb">
    <li><span class="fa fa-home"></span>  <a asp-controller="Home" asp-action="Index">首页</a></li>
    <li><a asp-controller="Category" asp-action="Index">栏目管理</a></li>
    <li class="active">添加栏目</li>
</ol>

<div class="alert alert-success fade in" role="alert">
    <h4><i class="fa fa-check"></i> 添加成功 </h4>
    <p>您已成功添加栏目【@Model.Name】</p>
    <p>
        <a href="@Url.Action("Add")" class="btn btn-default">添加栏目</a>
        <a href="@Url.Action("Details","Category",new {id=Model.CategoryId })" class="btn btn-default">修改栏目</a>
    </p>
</div>
@section aside{

    @Html.Partial("Aside")
}

四、其他

 

代码托管地址:https://git.oschina.net/ninesky/Ninesky

文章发布地址:http://www.ninesky.cn

                 http://mzwhj.cnblogs.com/

代码包下载:Ninesky2.4.rar

 

返回目录

posted @ 2017-01-06 14:55  洞庭夕照  阅读(5396)  评论(17编辑  收藏  举报