Upgrade Nuget
大石头 编写于 2024-09-28 08:10:09
NewLife.Cube
using System.ComponentModel;
using System.Runtime.Serialization;
using System.Text;
using System.Web.Script.Serialization;
using System.Xml.Serialization;
using NewLife.Collections;
using NewLife.Data;

namespace NewLife.Web;

/// <summary>分页器。包含分页排序参数,支持构造Url的功能</summary>
public class Pager : PageParameter, IExtend
{
    #region 名称
    /// <summary>名称类。用户可根据需要修改Url参数名</summary>
    public class __
    {
        /// <summary>排序字段</summary>
        public String Sort = "Sort";

        /// <summary>是否降序</summary>
        public String Desc = "Desc";

        /// <summary>页面索引</summary>
        public String PageIndex = "PageIndex";

        /// <summary>页面大小</summary>
        public String PageSize = "PageSize";
    }

    /// <summary>名称类。用户可根据需要修改Url参数名</summary>
    [XmlIgnore, ScriptIgnore, IgnoreDataMember]
    public static __ _ = new();
    #endregion

    #region 扩展属性
    /// <summary>参数集合</summary>
    [XmlIgnore, ScriptIgnore, IgnoreDataMember]
    public IDictionary<String, String> Params { get; set; } = new NullableDictionary<String, String>(StringComparer.OrdinalIgnoreCase);

    /// <summary>分页链接模版。内部将会替换{链接}和{名称}</summary>
    [XmlIgnore, ScriptIgnore, IgnoreDataMember]
    public String PageUrlTemplate { get; set; } = "<a href=\"{链接}\">{名称}</a>";

    private static readonly PageParameter _def = new();

    /// <summary>默认参数。如果分页参数为默认参数,则不参与构造Url</summary>
    [XmlIgnore, ScriptIgnore, IgnoreDataMember]
    public PageParameter Default { get; set; } = _def;

    /// <summary>获取/设置 参数</summary>
    /// <param name="key"></param>
    /// <returns></returns>
    public String this[String key]
    {
        get
        {
            if (key.EqualIgnoreCase(_.Sort))
                return Sort;
            else if (key.EqualIgnoreCase(_.Desc))
                return Desc + "";
            else if (key.EqualIgnoreCase(_.PageIndex))
                return PageIndex + "";
            else if (key.EqualIgnoreCase(_.PageSize))
                return PageSize + "";

            // 为了布尔型取空字符串时得到null。var enable = p["enable"]?.ToBoolean()
            var v = Params[key];
            return v.IsNullOrEmpty() ? null : v;
        }
        set
        {
            if (key.EqualIgnoreCase(_.Sort))
                Sort = value;
            else if (key.EqualIgnoreCase(_.Desc))
                Desc = value.ToBoolean();
            else if (key.EqualIgnoreCase(_.PageIndex))
                PageIndex = value.ToInt();
            else if (key.EqualIgnoreCase(_.PageSize))
                PageSize = value.ToInt();
            else
                Params[key] = value;
        }
    }
    #endregion

    #region 构造
    /// <summary>实例化</summary>
    public Pager() { }

    /// <summary>用另一个分页参数实例化</summary>
    /// <param name="pm"></param>
    public Pager(PageParameter pm) : base(pm)
    {
        if (pm is Pager p)
        {
            foreach (var item in p.Params)
            {
                this[item.Key] = item.Value;
            }
        }
    }

    /// <summary>借助字典实例化</summary>
    /// <param name="parameters"></param>
    public Pager(IDictionary<String, String> parameters)
    {
        if (parameters.TryGetValue(nameof(PageIndex), out var str)) PageIndex = str.ToInt();
        if (parameters.TryGetValue(nameof(PageSize), out str)) PageSize = str.ToInt();
        if (parameters.TryGetValue(nameof(Desc), out str)) Desc = str.ToBoolean();
        if (parameters.TryGetValue(nameof(StartRow), out str)) StartRow = str.ToLong();
        if (parameters.TryGetValue(nameof(Sort), out str)) Sort = str;
        //if (parameters.TryGetValue(nameof(OrderBy), out str)) OrderBy = str;

        Params = parameters;
    }
    #endregion

    #region 方法
    /// <summary>获取基础Url,用于附加参数</summary>
    /// <param name="where">查询条件,不包含排序和分页</param>
    /// <param name="order">排序</param>
    /// <param name="page">分页</param>
    /// <param name="excludes">要排除的参数</param>
    /// <returns></returns>
    public virtual StringBuilder GetBaseUrl(Boolean where, Boolean order, Boolean page, String[] excludes = null)
    {
        var sb = new StringBuilder();
        var dic = Params;
        //// 过滤
        //dic = PagerHelper.FilterSpecialChar(dic);

        // 先构造基本条件,再排序到分页
        if (where)
        {
            var ex = new List<String>
            {
                _.Sort,
                _.Desc,
                _.PageIndex,
                _.PageSize,
            };
            if (excludes != null && excludes.Length > 0) ex.AddRange(excludes);
            sb.UrlParamsExcept(dic, ex.ToArray());
        }
        if (order)
        {
            sb.UrlParams(dic, _.Sort, _.Desc);
            if (!dic.ContainsKey(_.Sort) && !Sort.IsNullOrEmpty()) sb.UrlParam(_.Sort, Sort);
            if (!dic.ContainsKey(_.Desc) && Desc) sb.UrlParam(_.Desc, Desc);
        }
        if (page)
        {
            sb.UrlParams(dic, _.PageIndex, _.PageSize);
            if (!dic.ContainsKey(_.PageIndex) && PageIndex > 1) sb.UrlParam(_.PageIndex, PageIndex);
            if (!dic.ContainsKey(_.PageSize) && PageSize > 0 && PageSize != 20) sb.UrlParam(_.PageSize, PageSize);
        }

        return sb;
    }

    /// <summary>获取排序的Url</summary>
    /// <param name="name"></param>
    /// <returns></returns>
    public virtual String GetSortUrl(String name)
    {
        // 首次访问该排序项,默认降序,重复访问取反
        var desc = true;
        if (Sort.EqualIgnoreCase(name)) desc = !Desc;

        var url = GetBaseUrl(true, false, true);
        // 默认排序不处理
        if (!name.EqualIgnoreCase(Default.Sort)) url.UrlParam(_.Sort, name);
        if (desc) url.UrlParam(_.Desc, true);
        return url.Length > 0 ? "?" + url.ToString() : "";
    }

    /// <summary>获取分页Url</summary>
    /// <param name="name"></param>
    /// <param name="index"></param>
    /// <returns></returns>
    public virtual String GetPageUrl(String name, Int64 index)
    {
        var url = GetBaseUrl(true, true, false);
        // 当前在非首页而要跳回首页,不写页面序号
        //if (!(PageIndex > 1 && index == 1)) url.UrlParam(_.PageIndex, index);
        // 还是写一下页面序号,因为页面Url本身就有,如果这里不写,有可能首页的href为空
        if (PageIndex != index) url.UrlParam(_.PageIndex, index);
        if (PageSize != Default.PageSize) url.UrlParam(_.PageSize, PageSize);

        var url2 = url.Length > 0 ? "?" + url.ToString() : "";

        var txt = PageUrlTemplate;
        txt = txt.Replace("{链接}", url2);
        txt = txt.Replace("{名称}", name);

        return txt;
    }

    /// <summary>获取分页Url</summary>
    /// <param name="name"></param>
    /// <returns></returns>
    public virtual String GetPage(String name)
    {
        if (PageIndex == 1)
        {
            if (name is "首页" or "上一页") return name;
        }
        if (PageIndex >= PageCount)
        {
            if (name is "尾页" or "下一页") return name;
        }

        if (PageIndex > 1)
        {
            if (name == "首页") return GetPageUrl("首页", 1);
            if (name == "上一页") return GetPageUrl("上一页", PageIndex - 1);
        }
        if (PageIndex < PageCount)
        {
            if (name == "尾页") return GetPageUrl("尾页", PageCount);
            if (name == "下一页") return GetPageUrl("下一页", PageIndex + 1);
        }

        return name;
    }

    /// <summary>转为分页模型</summary>
    /// <returns></returns>
    public PageModel ToModel() => new() { PageIndex = PageIndex, PageSize = PageSize, TotalCount = TotalCount, LongTotalCount = TotalCount.ToString() };
    #endregion

    #region IExtend接口
    /// <summary>参数集合</summary>
    [XmlIgnore, ScriptIgnore]
    [EditorBrowsable(EditorBrowsableState.Never)]
    public IDictionary<String, Object> Items => Params.ToDictionary(e => e.Key, e => (Object)e.Value);

    Object IExtend.this[String key] { get => Params[key]; set => Params[key] = value == null ? null : value + ""; }
    #endregion
}

/// <summary>分页模型</summary>
public class PageModel
{
    /// <summary>获取 或 设置 页面索引。从1开始,默认1</summary>
    /// <remarks>如果设定了开始行,分页时将不再使用PageIndex</remarks>
    public virtual Int32 PageIndex { get; set; }

    /// <summary>获取 或 设置 页面大小。默认20,若为0表示不分页</summary>
    public virtual Int32 PageSize { get; set; }

    /// <summary>获取 或 设置 总记录数</summary>
    public virtual Int64 TotalCount { get; set; }

    /// <summary>获取 或 设置 总记录数,字符串类型</summary>
    public virtual String LongTotalCount { get; set; }

    ///// <summary>获取 页数</summary>
    //public virtual Int64 PageCount { get; set; }
}