反射實體自動生成EasyUi DataGrid模板


用EasyUi Datagrid展示數據的時候總是要一下這樣一段代碼 

<table id="dt" class="easyui-datagrid">
    <thead>
        <tr>
            <th data-options="field:'Id',width:150,align:'center'">Id</th>
            <th data-options="field:'Name',width:150,align:'center'">Name</th>
            <th data-options="field:'Category',width:150,align:'center'">Category</th>
            <th data-options="field:'Price',width:150,align:'center'">Price</th>
            <th data-options="field:'Binary',width:150,align:'center'">Binary</th>
            <th data-options="field:'操作',width:80,align:'center',formatter:formatOperate" >操作</th>
        </tr>
    </thead>
</table>

 其實就是對應后台的一個實體類,具體展示就是對於的屬性名。我是一個比較懶的人,總是不喜歡做重復性的工作。剛好最近我又量用到EasyUi Datagrid,拷貝了兩次這個代碼我就煩了,於是想想怎么直接通過實體(ViewModel)來生成對應的easyui datagrid HTML代碼。就像MVC帶的驗證一樣,在類上打上自定義的屬性,最后在客戶端就能實現對應的驗證功能。其實就是根據自定義的屬性在客戶端生成對應的js代碼。為了避免反復寫html代碼這個繁瑣的工作,於是我開發了一個簡版的datagrid模板生成器。每次只要你想偷懶的時候總是能找到方法。

於是我花了點時間來實現這個想法,並投入到項目中使用。開始沒有考慮太多,只是臨時滿足我現在的需求,以下是主要代碼。就是通過反射取出實體的所有字段,然后遍歷每個字段,拿到字段上自定義的屬性,並挨個處理。根據easyui datagrid模板要求,我們主要是設置table的表頭,column每個字段

 

namespace WebSeat.Web.Core.EasyUi
{
#pragma warning disable 693
    public class GenerateDataGrid<T> where T : new()
#pragma warning restore 693
    {

        #region Tools Methods

        public static IEnumerable<PropertyInfo> GetPropertyInfoList(T entity)
        {
            return
                entity.GetType()
                    .GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase)
                    .Where(propertyInfo => propertyInfo.Name.ToLower() != "id"); //Id為主鍵,不能插入。寫死了,這個可以做相應修改,主要在字段刪改時用到
        }

        public static IEnumerable<PropertyInfo> GetAllPropertyInfoList(T entity)
        {
            return
                entity.GetType()
                    .GetProperties(BindingFlags.Public | BindingFlags.Instance | BindingFlags.IgnoreCase);
        }


        #endregion

        public static string GetDataGridTemplate()
        {
            return GetDataGridTemplate(new T());
        }

        /// <summary>
        ///     生成Datagrid的模板
        /// </summary>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static string GetDataGridTemplate(T entity)
        {
            var sb = new StringBuilder();
            //先獲取類的Attribute
            CustomAttributeData entityType =
                typeof(T).CustomAttributes.FirstOrDefault(a => a.AttributeType == typeof(DataOptionsAttribute));

            #region 對實體的Attibute屬性進行處理

            //類的這個自定義屬性不為空
            bool isShowNotAttr = true;
            string options = string.Empty;
            if (entityType != null)
            {
                if (entityType.NamedArguments != null)
                {
                    foreach (CustomAttributeNamedArgument v in entityType.NamedArguments)
                    {
                        if ((String.CompareOrdinal(v.MemberName, "IsShowNotAttr")) == 0)
                        {
                            isShowNotAttr = v.TypedValue.Value is bool && (bool)v.TypedValue.Value;
                            continue;
                        }
                        if ((String.CompareOrdinal(v.MemberName, "Options")) == 0)
                        {
                            options = v.TypedValue.Value as string;
                        }
                    }
                }
            }
            options = string.IsNullOrWhiteSpace(options) ? "" : options;

            #endregion

            //獲取所有類的Property
            IEnumerable<PropertyInfo> properties = GetAllPropertyInfoList(entity);

            //如果設置有不顯示沒有dataoptions標記的,如果有些字段不顯示出來,這個就有作用了。
            if (!isShowNotAttr)
            {
                properties = properties.Where(n => n.CustomAttributes.Any(a => a.AttributeType == typeof(DataOptionsAttribute)));
            }
            //沒有打標記的也要取出來,轉換成key-value集合,key是字段名,value是它的GetCustomAttributes。DataOptionsAttribute 自定義屬性,見下面代碼
            Dictionary<string, List<Attribute>> colDicOpts = properties.ToDictionary(property => property.Name,
                property =>
                {
                    var list = new List<Attribute>
                    {
                        property.GetCustomAttributes(typeof (DataOptionsAttribute), false).FirstOrDefault() as DataOptionsAttribute,
                        property.GetCustomAttributes(typeof (DisplayAttribute), false).FirstOrDefault() as DisplayAttribute
                    };
                    return list;
                }
                );
               
//開始拼接,天添加talbe 部分,id和其他的自己可改下代碼,做成更靈活的 sb.AppendLine("<table id=\"dt\" class=\"easyui-datagrid\" data-options=\"" + options + "\" > <thead> <tr>"); //挨個遍歷 處理,添加 tr foreach (PropertyInfo pro in properties) { //get CustomAttributes var custAttrs = colDicOpts.SingleOrDefault(n => n.Key == pro.Name); sb.AppendLine(AppenedTemplate(Template.DataGridTh, custAttrs, pro)); } sb.AppendLine(@" </tr> </thead> </table>"); return sb.ToString(); } private static string AppenedTemplate(string template, KeyValuePair<string, List<Attribute>> attributes, PropertyInfo proinfo = null) { var displayName = attributes.Value.SingleOrDefault(n => n is DisplayAttribute) as DisplayAttribute; //設置字段顯示的名稱,自己直接設置DisplayAttribute,這個大家肯定很熟悉的屬性,如果設置有DisplayAttribute就顯示設置的Name否則就是默認字段名稱 string str = Template.RegV.Replace(template, displayName != null ? displayName.Name : attributes.Key); //設置顯示的字段field,就是行column顯示的字段,這個都是實在字段名,字段名就是key str = Template.RegF.Replace(str, attributes.Key); var dataOptions = attributes.Value.SingleOrDefault(n => n is DataOptionsAttribute) as DataOptionsAttribute; //由於我自己的需要,我要對DateTime類型進行特殊處理 if (proinfo != null && proinfo.PropertyType == typeof(DateTime)) { //沒有自定義dataOptions屬性的值 if (dataOptions == null) { //WebJs.Format.formatTime 自己的js時間格式化函數 str = Template.RegD.Replace(str, "formatter:WebJs.Format.formatTime");//默認時間格式 } else { str = dataOptions.Options.IndexOf("formatter", StringComparison.CurrentCultureIgnoreCase) >= 0 ? //已經設置formatter Template.RegD.Replace(str, dataOptions.Options) : //默認設置formatter Template.RegD.Replace(str, dataOptions.Options + "formatter:WebJs.Format.formatTime"); } } else { //替換data-option 的值, 如果為空就直接替換為空 str = dataOptions == null ? //把前面的逗號也替換掉 Template.RegDi.Replace(str, string.Empty) : Template.RegD.Replace(str, dataOptions.Options); } return str; } } }

 

DataOptionsAttribute 為自定義屬性,Options 的值,到時就直接替換到模板的 data-options里。既是改字段在表頭顯示的漢字。

namespace WebSeat.Web.Core.Attributes
{
    [AttributeUsage(AttributeTargets.All, Inherited = false, AllowMultiple = false), Serializable]
    public class DataOptionsAttribute : Attribute
    {
        public DataOptionsAttribute()
        {
            IsShowNotAttr = true;
        }
        /// <summary>
        /// data-options
        /// </summary>
        public string Options { get; set; }
        /// <summary>
        /// 是否顯示類屬性上沒有打標記的元素
        /// </summary>
        public bool IsShowNotAttr { get; set; }
    }
}

 

//模板類 

//模板,這里只用到這一部分
public
static class Template { public const string DataGridTh = "<th data-options=\"field:'{field}',align:'center',{d}\" > {v}</th>"; public static Regex RegV = new Regex(@"\{v\}"); public static Regex RegF = new Regex(@"\{field\}"); public static Regex RegD = new Regex(@"\{d\}"); public static Regex RegDi = new Regex(@",\{d\}"); }

 

最后做成HtmlHelper 擴展方法,方便頁面調用 ,這里貼出關鍵代碼

namespace WebSeat.Web.Core.Extends
{
    public static class ExtendClass
    {     /// <summary>
        /// HtmlHelper擴展方法
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="html"></param>
        /// <param name="entity"></param>
        /// <returns></returns>
        public static MvcHtmlString CreateDataGridTemplate<T>(this HtmlHelper html, T entity) where T : new()
        {
            return new MvcHtmlString(GenerateDataGrid<T>.GetDataGridTemplate(entity));
        }       
    }
}

 

先是一個基類,然后是一個viewmodel繼承基類,注意里面自定義的DataOptions 屬性

    /// <summary>
    ///     模型基類
    /// </summary>
    public abstract class BaseViewModel
    {
        /// <summary>
        ///     ID
        /// </summary>
        [Display(Name = "主鍵ID")]
        public int Id { get; set; }

        /// <summary>
        ///     添加數據時間
        /// </summary>
        [Display(Name = "添加數據時間")]
        public DateTime CreateTime { get; set; }
    }
    /// <summary>
    ///     班級列表實體
    /// </summary>
    public class ClassGradeListViewModel : BaseViewModel
    {
        /// <summary>
        ///     班級名稱
        /// </summary>
        [Display(Name = "班級名稱")]
        public string Name { get; set; }

        /// <summary>
        ///     班級圖片地址
        /// </summary>
        [DataOptions(Options = "ddd:'sdfdsf',xj:'321'")]//對應到列上的data-options 就寫到這里,類也是一樣
        public string PicUrl { get; set; }
    }

 

以下是頁面代碼,我對easyUi做了一點封裝,@Html.CreateDataGridTemplate(new ClassGradeListViewModel())//這句代碼生成datagrid 模板

@using WebSeat.FlipCourse.WebApi.ViewModels
@using WebSeat.Web.Core.EasyUi
@using WebSeat.Web.Core.Extends
@{
    ViewBag.Title = "LoadClassList";
    Layout = "~/Areas/TestApi/Views/Shared/_EasyUiLayout.cshtml";
}

@section searchs{
    StudentId:<input type="text" id="StudentId" name="StudentId"  init="請輸入StudentId" class="easyui-numberbox" validtype="isId" />
    ClassId:<input type="text" id="ClassId" name="ClassId" value="9" init="請輸入ClassId" class="easyui-numberbox" validtype="isId" />
    <a href="javascript:;"class="easyui-linkbutton" data-options="@IconCls.Search" onclick="LoadClassList()">LoadClassList</a>
}
//一句話搞定模板生成,再也不用每次寫一堆html代碼了 @Html.CreateDataGridTemplate(
new ClassGradeListViewModel())//這句代碼生成datagrid 模板 @section scripts{ <script type="text/javascript"> function LoadClassList(opts) { WebJs.EasyUi.loadpageList('/api/Student/LoadClassList'); } </script> }

 

//對應部分js代碼,去掉了很多,大多是根據我們自己的需求來寫的

//EasyUi 
WebJs.EasyUi = (function () {
    //datagrid的默認配置
    var defaultOpts = {
        collapsible: true,
        rownumbers: true,
        singleSelect: true,
        pagination: true,
        striped: true,
        fit: true,
        border: false,
        method: 'get',
        toolbar: "#Search",
        idField: 'Id',
        pageSize: 10,
        pageNumber: 1,
        headers: {
            "contentType": "application/json; charset=utf-8",
            "token": WebJs.Utils.GetCookie('Token') || "token"
        },
        loadFilter: filterData,
        reloadFunc: function () { return false; },
        onLoadError: WebJs.Common.ShowErrors
    },
        loadTimmer, //記錄 timmer防止反復加載
        loadTime = 0, //多少毫秒加載一次
        accordion = '#aa',
        tabId = '#tabs',
        layOutId = '#body',
        dtId = '#dt';
    //取得Id
    function getdg(id) { //取得datagrid的Id  默認為 dt,  
        id = id || dtId;
        return $(id);
    }   
    //擴展EasyUI的驗證
    function extendEasyUiValidate() {
        $.extend($.fn.validatebox.defaults.rules, {
            pattern: {
                validator: function (value, param) {
                    var re = new RegExp('' + param[0], 'gi');
                    //if (param[1]) { msg = param[1]; }
                    //WebJs.Dialog.Tip(param[0] +param[1]+ "===" + value + "---" + re.test(value));
                    return re.test(value);
                },
                message: '輸入格式不正確'
            },
            maxLength: {
                validator: function (value, param) {
                    return value.trim().length <= param[0];
                },
                message: '最多只能輸入{0}個字符'
            },
            list: {
                validator: function (value, param) {
                    return /^\d[\d|,?]*/gi.test(value);
                },
                message: '輸入格式不對,數字之間都好隔開'
            },
            isId: {
                validator: function (value, param) {
                    return /^[1-9]\d*$/gi.test(value) && ((value | 0) > 0 && (value | 0) < 2e6);
                },
                message: 'Id必須是數字,類型為int'
            },
            equalTo: {
                validator: function (value, param) {
                    return value == $(param[0]).val();
                },
                message: '兩次輸入的字符不一至'
            },
            number: {
                validator: function (value, param) {
                    return /^\d+$/.test(value);
                },
                message: '請輸入數字'
            }
        });
    }
    //專為我們自己的分頁而生
    function loadpageList(url, id) {
        var form = $('#SearchForm');
        if (form.children().length && !form.form('validate')) {
            return;
        }
        var para = new BaseParams(),//和后台分頁實體對應
            formjson = form.serializeJson(),//serializeJson 需要自己擴展
            params = $.extend(para, formjson),cfgs = {
               method: 'post',
               queryParams: params
            };
        //設置datagrid的分頁參數 
        cfgs.pageSize = params.PageSize || 10;
        cfgs.pageNumber = params.PageIndex || 1;
        WebJs.EasyUi.LoadData(url,cfgs,id);
    }
    return {
        //加載datagrid的數據
        LoadData: function (url, opts, id) {
            //clearTimeout(loadTimmer);
            var defaults = $.extend(defaultOpts, opts || {});
            url && (defaults.url = url);
            //loadTimmer = setTimeout(function () {
            //可以對datagrid進行緩存
            getdg(id).datagrid(defaults);
            //}, loadTime);
        },
        //加載分頁數據
        loadpageList: loadpageList,      
        filterData: filterData
    };
})();

//最終頁面效果, 生成的datagrid模板

 

 

調用一句話就生成datagrid模板,這個不適合復雜的模板。

我暫時就想到這些,這樣可以在項目組偷個懶。

其實項目中很多地方都可以這樣,只要有想法自己就去做,不管做的好還是差,做成功還是有點小成就感。

做完這個東西並且在項目中實際運用,最后效果良好,各種好使,當然后來也有很多改動。

 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM