1. 程式人生 > >ABP module-zero +AdminLTE+Bootstrap Table+jQuery許可權管理系統第十二節--小結,Bootstrap Table之角色管理以及module-zero角色管理

ABP module-zero +AdminLTE+Bootstrap Table+jQuery許可權管理系統第十二節--小結,Bootstrap Table之角色管理以及module-zero角色管理

寫在前面的話

          很多人說ABP不適合高併發大型,有一定的道理,但是我覺得還是可以的,就看架構師的能力了,我之前公司就是ABP絕對百萬資料級專案,是一個線上教育網站,涉及到平臺,學院,院系,班級,課程,學生等,一個平臺多少大學,一個大學多少院系,一個院系多少班級多少課程,其負責程度一點都不簡單,不說了,那是大神,比我在園子看到絕對大多數架構師都強悍.是我等仰望都物件.但是這不是停下腳步仰望的理由,只會是我們追求更強的腳步.

          軟體開發中會有大量機械重複的工作,也有大量重複的程式碼,使用者需求越來越高,我們要做出的響應也越來越複雜,這就需要我們去整合更多的東西來應付工作的需要。

         每當我們要開發一個新的系統的時候,我們要用到以前用到過的東西,比如mvc,ioc,日誌,資料庫的對映,日誌管理,訊息佇列元件等等。我們更希望把更多時間時間用來處理業務邏輯,而不是花在軟體結構,日誌,容錯等問題。

          很多大公司都有自己的基礎框架,自己公司封裝了很多東西,像我們公司的日期元件,彈窗,表格樣式,分頁等等都是已經封裝好了的,再後期開發的時候我們只需要去呼叫一下就可以了,不需要每個控制元件都自己再花時間和精力去封裝。

          這節我就來說一下在ABP module-zero基礎上做AdminLTE+Bootstrap Table的系統,算是前面十一節的總結和擴充套件.

          首先我們依舊去官網根據abp模板建立解決方案.勾選上module-zero,然後建立資料庫連線,還原nuget包,然後update-database建立資料庫.

          接下來就是建立指令碼更新資料庫了,以上這些都在前面的章節講到了,我就不做累述了,不明白可回去一下第一節.

          然後在home控制器下建ActionResult login,新增一個試圖頁面.加上自己的登陸頁面樣式js等,這裡我們就不用mvc的形式再在去請求控制器 了,我們直接請求webapi的登陸方法了.是的就是已經搭好了swagger的然後請求/swagger/ui/index的Account介面.如圖.

         

abp  javascript ajax的封裝及應用

 既然前面講到ABP 對javascript ajax的封裝

var newPerson = {
    name: 'Dougles Adams',
    age: 42
};

abp.ajax({
    url: '/People/SavePerson',
    data: JSON.stringify(newPerson)
}).done(function(data) {
    abp.notify.success('created new person with id = ' + data.personId);
});

  其實這裡我們還可以做一些再次封裝,方便在專案中去使用,就以登陸為例效果如下。

   abp.ui.block($('#login'));
                var url = "/api/Account";
                var login = function (para, ajaxtype,posturl) {
                    return abp.ajax({
                        url: posturl,
                        type: ajaxtype,
                        async: false,
                        data: JSON.stringify(para)
                    });
                };

                var loginModel = {
                    "tenancyName": "",
                    "usernameOrEmailAddress": $("#userName").val(),
                    "password": $("#password").val()
                };

               
                abp.ui.setBusy(
                    $('#login'),
                    login(loginModel, "post", url).done(function (data) {
                        abp.auth.setToken("Bearer " + data);
                       window.location.href = "/admin/userinfo/index"
                    }),
                  
                );

abp.ui.block

          當然這是在ABP原來封裝的效果上加上的,細心的你已經發現這裡多了兩個東西,一個是abp.ui.block,另外一個是abp.ui.setBusy,這其實是一個阻止使用者重複提交,和正在提交繁忙狀態,

 其實就是一個遮罩層。

       

             這裡是ABP整合的jquery.blockUI.js外掛,該API使用一個透明的塗層(transparent overlay)來阻塞整個頁面或者該頁面上的一個元素。這樣,使用者的點選就無效了。當儲存一個表單或者載入一個區域(一個div或者整個頁面)時這是很有用的。比如

           另外abpjs中也對blockUI做了一些常用方法的封裝,設定阻塞abp.ui.block,取消阻塞abp.ui.unblock ,設定繁忙狀abp.ui.setBusy 和解除繁忙狀態abp.ui.clearBusy

 abp.ui.block = function (elm) {
        abp.log.warn('abp.ui.block is not implemented!');
    };

    abp.ui.unblock = function (elm) {
        abp.log.warn('abp.ui.unblock is not implemented!');
    };

    /* UI BUSY */
    //Defines UI Busy API, not implements it

    abp.ui.setBusy = function (elm, optionsOrPromise) {
        abp.log.warn('abp.ui.setBusy is not implemented!');
    };

    abp.ui.clearBusy = function (elm) {
        abp.log.warn('abp.ui.clearBusy is not implemented!');
    };
abp.ui.block(); //阻塞整個頁面
abp.ui.block($('#MyDivElement')); //可以使用jQuery 選擇器..
abp.ui.block('#MyDivElement'); //..或者直接使用選擇器
abp.ui.unblock(); //解除阻塞整個頁面
abp.ui.unblock('#MyDivElement'); //解除阻塞特定的元素

 abp.blockUI

        UI Block API預設使用jQuery的blockUI外掛實現的。要是它生效,你應該包含它的javascript檔案,然後在頁面中包含abp.blockUI.js作為介面卡。

        另外一個就是busy 該API用於使得某些頁面或者元素處於繁忙狀態。比如,你可能想阻塞一個表單,然後當提交表單至伺服器時展示一個繁忙的指示器。例子:

abp.ui.setBusy('#MyLoginForm');
abp.ui.clearBusy('#MyLoginForm');

       效果就是上面的繁忙效果。

       該引數應該是一個選擇器(如‘#MyLoginForm’)或者jQuery選擇器(如$('#MyLoginForm'))。要使得整個頁面處於繁忙狀態,你可以傳入null(或者'body')作為選擇器。setBusy函式第二個引數接收一個promise(約定),當該約定完成時會自動清除繁忙的狀態。因為abp.ajax返回promise,我們可以直接將它作為promise傳入。要學習慣於promise更多的東西,檢視jQuery的。

      UI Busy API是使用實現的。要讓它生效,應該包含它的javascript檔案,然後在頁面中包含abp.spin.js作為介面卡。

      經過上面的努力,我們得登陸也已經做好了。登陸成功之後我們要做事的事情就是一個儲存token另外一個就是路由的重定向了。

     Abp.AuthToken與cookie

      token在ABP中很重要,我們在請求 /api/Account會反饋一個token,我們在登陸的時候就把token存到cookie中,以方便後面的使用。如登陸中的 abp.auth.setToken("Bearer " + data); 那ABP是怎麼樣設定cookie的了,這裡也做了封裝。

 abp.auth.tokenCookieName = 'Abp.AuthToken';

    abp.auth.setToken = function (authToken, expireDate) {
        abp.utils.setCookieValue(abp.auth.tokenCookieName, authToken, expireDate, abp.appPath);
    };

    abp.auth.getToken = function () {
        return abp.utils.getCookieValue(abp.auth.tokenCookieName);
    }

    abp.auth.clearToken = function () {
        abp.auth.setToken();
    }

    這裡面就包含了token常用的存取和清除的方法。頁面上快取的cookie名字就是Abp.AuthToken,獲取的時候可以直接獲取。

 model和DTO

     我們在已經添加了域,所以這裡登陸成功之後直接把url指向/admin/userinfo/index。當然在/Areas/Common/Views/Layout裡面我們已經AdminLTE的佈局了,包括選單已經加載出來了,其實現在服務層的時候漏了一些東西,我們這裡補上,ABP既然是一個框架

那麼在建立Service的時候自然要包含基礎增刪查改的方法,那麼這時候IAsyncCrudAppService就派上用場了。這裡我們以模板IModulesService講一下。首先我們建立好model和DTO

using Abp.Domain.Entities;
using System;
using System.Collections.Generic;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JCms.Meuns
{
    public class Meun : Entity<int>, IMayHaveTenant
    {
        public int? ParentId { get; set; }
        [Required]
        [StringLength(20)]
        public string Name { get; set; }
        [Required]
        [StringLength(50)]
        public string LinkUrl { get; set; }
        [StringLength(100)]
        public string Description { get; set; }
        public bool IsMenu { get; set; }
        public int Code { get; set; }
        public bool Enabled { get; set; }

        public DateTime UpdateDate { get; set; }
        public int? TenantId { get; set; }

    
    }
}

DTO:

using Abp.Application.Services.Dto;
using Abp.AutoMapper;
using Abp.Domain.Entities;
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.ComponentModel.DataAnnotations;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace JCms.Meuns
{
    /// <summary>
    /// 選單
    /// </summary>
    [Serializable]
    [AutoMapFrom(typeof(Meun))]
    public class MeunDto : EntityDto<int>
    {  /// <summary>
       /// id
       /// </summary>
        public int Id { get; set; }
        /// <summary>
        /// 父模組Id
        /// </summary>
        public int? ParentId { get; set; }
        /// <summary>
        /// 名稱
        /// </summary>
        [Required]
        [StringLength(20)]
        public string Name { get; set; }
        /// <summary>
        /// 連結地址
        /// </summary>
        [Required]
        [StringLength(50)]
        public string LinkUrl { get; set; }

        /// <summary>
        /// 是否是選單
        /// </summary>
        public bool IsMenu { get; set; }
        /// <summary>
        /// 模組編號
        /// </summary>
        public int Code { get; set; }
        /// <summary>
        /// 描述
        /// </summary>
        [StringLength(100)]
        public string Description { get; set; }
        /// <summary>
        /// 是否啟用
        /// </summary>
        public bool Enabled { get; set; }

        public DateTime UpdateDate { get; set; }

        //public virtual MeunDto ParentModule { get; set; }
        //public List<MeunDto> ChildModules { get; private set; }
        public List<MeunDto> children { get; set; }
    }
}

       這裡要注意的如果model集成了某個介面,那麼DTO也要繼承這個介面的DTO,不然再繼承IAsyncCrudAppService就會報錯。

       比如面的model繼承Entity<int> 那麼DTO也要繼承EntityDto<int>.

IAsyncCrudAppService

        然後我們看一下IAsyncCrudAppService需要哪麼引數。

     這裡都可以看得很清楚了,包括增刪查改的DTO這裡我比較懶,都用了一個。

介面:

 public interface IModulesService : IAsyncCrudAppService<MeunDto, int, PagedResultRequestDto, MeunDto, MeunDto> // IApplicationService
    {

    }

實現

    public class ModulesService : AsyncCrudAppService<Meun, MeunDto, int, PagedResultRequestDto, MeunDto, MeunDto>, IModulesService
    {
        public ModulesService(IRepository<Meun, int> repository) : base(repository)
        {
        }
    }

apiJCMSWebApiModule

    然後,我們在apiJCMSWebApiModule方法下加上.WithConventionalVerbs() 這樣我們就可以看到特定的HTTP字首,不然全是post,HTTP動詞是通過方法名的字首決定的:

  • Get:方法名以Get開頭。
  • Put:方法名以Put或Update開頭。
  • Delete:方法名以Delete或Remove開頭。
  • Post:方法名以Post或Create開頭。
  • 否則,Post是HTTP動詞的預設值。

       我們可以通過對特定的方法使用WithVerb方法或者HTTP特性來覆蓋上述慣例。

         就這樣,我們在業務層中常用的增刪改查的方法就誕生了。

           驗證一下,傳入引數,結果沒毛病,可用。當然這裡根據自己的需要可以重寫這些方法的。頁面和前面的頁面差不多,沒啥講的,這裡頁面增刪改查都已經實現。

           首先我們看一下userinfo 頁面,這裡我們也根據abp  ajax的封裝和 swagger的應用做了一些改變,首先看頁面,我們不通過控制器去獲取建立和修改刪除的方法了。控制器只有一個帶分頁的獲取的方法 這是因為Bootstrap table必須要以這樣的json結構返回

控制器方法

using Abp.Application.Services.Dto;
using Abp.Web.Models;
using Abp.Web.Security.AntiForgery;
using JCMS.Authorization.Users;
using JCMS.Sessions.Dto;
using JCMS.Users;
using JCMS.Users.Dto;
using JCMS.Web.Controllers;
using System;
using System.Collections.Generic;
using System.Linq;
using System.Web;
using System.Web.Mvc;

namespace JCMS.Web.Areas.Admin.Controllers
{
    public class UserInfoController : JCMSControllerBase
    {

        private readonly IUserAppService _UserAppService;

        public UserInfoController(

           IUserAppService UserAppService)
        {
            _UserAppService = UserAppService;
        }
        public ActionResult Index()
        {
            return View();
        }

        [DisableAbpAntiForgeryTokenValidation]
        [HttpGet]
        [DontWrapResult] 
        public JsonResult GetUsersList()
        {
            string pageNumber = string.IsNullOrWhiteSpace(Request["pageNumber"]) ? "0" : Request["pageNumber"];
            string pageSize = string.IsNullOrWhiteSpace(Request["pageSize"]) ? "20" : Request["pageSize"];
            List<UserDto> Userlist = new List<UserDto>();
            Userlist = _UserAppService.GetAllList();
            int totaldata = Userlist.Count();
            Userlist = Userlist.Skip(int.Parse(pageNumber) * int.Parse(pageSize)).Take(int.Parse(pageSize)).ToList();
            var result = new { total = totaldata, rows = Userlist };
            return Json(result, JsonRequestBehavior.AllowGet);
        }
    }
}

               DontWrapResult標籤打上的,不需要abp做的特殊封裝。js頁面的一些方法。

<script type="text/javascript">

    $(function () {
        //1.初始化Table
        var oTable = new TableInit();
        oTable.Init();

        //2.初始化Button的點選事件
        var oButtonInit = new ButtonInit();
        oButtonInit.Init();

    });
   var Url = "@Url.Action("GetUsersList")";
   // var Url = "/api/services/app/user/GetAll()";
    var TableInit = function () {
        var oTableInit = new Object();
        //初始化Table
        oTableInit.Init = function () {
            $('#tb_departments').bootstrapTable({
               // url: '../User/GetUsersList',
                url: Url,         //請求後臺的URL(*)
                method: 'get',                      //請求方式(*)
                toolbar: '#toolbar',                //工具按鈕用哪個容器
                striped: true,                      //是否顯示行間隔色
                cache: false,                       //是否使用快取,預設為true,所以一般情況下需要設定一下這個屬性(*)
                pagination: true,                   //是否顯示分頁(*)
                sortable: false,                     //是否啟用排序
                sortOrder: "asc",                   //排序方式
                queryParams: oTableInit.queryParams,//傳遞引數(*)
                sidePagination: "server",           //分頁方式:client客戶端分頁,server服務端分頁(*)
                pageNumber: 1,                       //初始化載入第一頁,預設第一頁
                pageSize: 2,                       //每頁的記錄行數(*)
                pageList: [10, 25, 50, 100],        //可供選擇的每頁的行數(*)
                search: true,                       //是否顯示錶格搜尋,此搜尋是客戶端搜尋,不會進服務端,所以,個人感覺意義不大
                strictSearch: true,
                showColumns: true,                  //是否顯示所有的列
                showRefresh: true,                  //是否顯示重新整理按鈕
                minimumCountColumns: 2,             //最少允許的列數
                clickToSelect: true,                //是否啟用點選選中行
                height: 500,                        //行高,如果沒有設定height屬性,表格自動根據記錄條數覺得表格高度
                uniqueId: "ID",                     //每一行的唯一標識,一般為主鍵列
                showToggle: true,                    //是否顯示詳細檢視和列表檢視的切換按鈕
                cardView: false,                    //是否顯示詳細檢視
                detailView: false,                   //是否顯示父子表
                columns: [{
                    checkbox: true
                }, {
                    field: 'UserName',
                    title: '姓名'
                }, {
                    field: 'EmailAddress',
                    title: '郵箱'
                }, {
                    field: 'Surname',
                    title: '真是姓名'
                }, {
                     field: 'Name',
                    title: '角色'
                }, ]
            });
        };

        //得到查詢的引數
        oTableInit.queryParams = function (params) {
            var temp = {   //這裡的鍵的名字和控制器的變數名必須一直,這邊改動,控制器也需要改成一樣的
                limit: params.limit,   //頁面大小
                offset: params.offset,  //頁碼
                departmentname: $("#txt_search_departmentname").val(),
                statu: $("#txt_search_statu").val()
            };
            return temp;
        };
        return oTableInit;
    };


    var ButtonInit = function () {
        var oInit = new Object();
        var postdata = {};

        oInit.Init = function () {
            //初始化頁面上面的按鈕事件
            //查詢角色
            $("#btn_query").click(function () {
                var actionUrl = "@Url.Action("GetUsersList")";
                m_pagerow = 0;
                $("#tb_departments").bootstrapTable('refresh', { url: actionUrl });

            });
            //新增角色
            $("#btn_add").click(function () {
                $("#id").val("");
                $("#txt_Surname").val("");
                $("#txt_Name").val("");
                $("#txt_UserName").val("");
                $("#txt_isDeleted").val("");
                $("#myModalLabel").text("新增");
                $('#myModal').modal();
            });
            //新增角色
            $("#btn_submit").click(function () {

                var Id = $("#id").val() == "" ? 0 : $("#id").val();
                var Url = "/api/services/app/user/Update";
                var ajaxtype = "put";
                if ($("#id").val() == "") {
                    Url = "/api/services/app/user/Create";
                    ajaxtype = "post";
                }
                var careteorUpdatePerson = function (person, ajaxtype) {
                    return abp.ajax({
                        url: Url,
                        type: ajaxtype,
                        async: false,
                        data: JSON.stringify(person)
                    });
                };

                var newPerson = {
                     "id": $("#id").val(),
                     "UserName": $("#txt_Surname").val(),
                     "EmailAddress" : $("#txt_Name").val(),
                     "Name": $("#txt_UserName").val(),
                     "Surname": "test",
                     "Password": "123456"
                };

                careteorUpdatePerson(newPerson, ajaxtype).done(function (data) {
                        toastr.warning('操作成功!');
                        var actionUrl = "@Url.Action("GetUsersList")";
                        $("#tb_departments").bootstrapTable('refresh', { url: actionUrl });
                });
            });

            //編輯角色
            $("#btn_edit").click(function () {
                var arrselections = $("#tb_departments").bootstrapTable('getSelections');
                if (arrselections.length > 1) {
                    toastr.warning('只能選擇一行進行編輯');
                    return;
                }
                if (arrselections.length <= 0) {
                    toastr.warning('請選擇有效資料');
                    return;
                }
                $("#id").val(arrselections[0].id);
                $("#txt_Surname").val(arrselections[0].UserName);
                $("#txt_Name").val(arrselections[0].EmailAddress);
                $("#txt_UserName").val(arrselections[0].Name);
                $("#txt_isDeleted").val(arrselections[0].Id);

                $("#myModalLabel").text("修改");
                $('#myModal').modal();       
            });


            //通常我們使用ajax會按照如下寫法,做一個簡單的封裝來重用ajax,此處框架可以幫你生成簡單的呼叫方法
            var deletePerson = function (person) {
                return abp.ajax({
                  //  url: abp.appPath + '/api/services/app/user/Delete',
                    url:'/api/services/app/user/Delete',
                    type: 'delete',
                    async: false,
                    data: JSON.stringify(person)
                });
            };
            //刪除角色
            $("#btn_delete").click(function () {
                var arrselections = $("#tb_departments").bootstrapTable('getSelections');
                if (arrselections.length > 1) {
                    toastr.warning('只能選擇一行進行編輯');
                    return;
                }
                if (arrselections.length <= 0) {
                    toastr.warning('請選擇有效資料');
                    return;
                }
                var Id = arrselections[0].id;
                var newPerson = {
                    "id": Id
                };
              //直接呼叫方法,如何生成上面的呼叫方法可以參考原始碼中的Abp.Web.Api專案中/ WebApi/ Controllers/ Scripting/ jQuery下的實現
                deletePerson(newPerson).done(function (data) {
                        toastr.warning('操作成功!');
                        var actionUrl = "@Url.Action("GetUsersList")";
                        $("#tb_departments").bootstrapTable('refresh', { url: actionUrl });
                });

            });


            //許可權授權
            $("#btn_authorize").click(function () {
                var arrselections = $("#tb_departments").bootstrapTable('getSelections');
                if (arrselections.length > 1) {
                    toastr.warning('只能選擇一個角色進行授權');
                    return;
                }
                if (arrselections.length <= 0) {
                    toastr.warning('請選擇有效資料');
                    return;
                }
                var actionUrl = "@Url.Action("AuthorizePermission")";
                var param = { id: arrselections[0].Id };
                ShowModal_Authorize(actionUrl, param, "許可權授權");
            });
            //模態框中“許可權授權”儲存
            var $modal = $("#authorizeModal");
            $("#btnSave", $modal).click(function () {
                var actionUrl = "@Url.Action("AuthorizePermission")";
                SaveModal_Authorize(actionUrl);
            });
            //模態框中“新增編輯角色”儲存
            var $formmodal = $("#modal-form");
            $("#btnSave", $formmodal).click(function () {
                var $tb = $("#tb_departments");
                SaveModal($tb);
            });

            /*******彈出表單*********/
            function ShowModal(actionUrl, param, title) {
                debugger;
                var $modal = $("#modal-form");
                //表單初始化
                $(".modal-title", $modal).html(title);
                $("#modal-content", $modal).attr("action", actionUrl);

                $.ajax({
                    type: "GET",
                    url: actionUrl,
                    data: param,
                    beforeSend: function () {
                    },
                    success: function (result) {
                        debugger;
                        $("#modal-content").html(result);
                        $('#modal-form').modal('show');
                    },
                    error: function () {

                    },
                    complete: function () {

                    }
                });
            }


        };

        return oInit;
    };



</script>
View Code

 api,services應用

     以careteorUpdatePerson方法為例,我都是封裝好的。 呼叫的時候直接請求  "/api/services/app/user/Create"地址就可以了,其實這些程式碼都是可以複用的,我們也可以封裝到一個共同的js頁面。我這裡就沒有做這麼詳細。

   var careteorUpdatePerson = function (person, ajaxtype) {
                    return abp.ajax({
                        url: Url,
                        type: ajaxtype,
                        async: false,
                        data: JSON.stringify(person)
                    });
                };

                var newPerson = {
                     "id": $("#id").val(),
                     "UserName": $("#txt_Surname").val(),
                     "EmailAddress" : $("#txt_Name").val(),
                     "Name": $("#txt_UserName").val(),
                     "Surname": "test",
                     "Password": "123456"
                };

                careteorUpdatePerson(newPerson, ajaxtype).done(function (data) {
                        toastr.warning('操作成功!');
                        var actionUrl = "@Url.Action("GetUsersList")";
                        $("#tb_departments").bootstrapTable('refresh', { url: actionUrl });
                });
            });

首先我們傳入的引數newPerson 必須與接口裡面的需要的引數一致。而且接口裡面自帶驗證。資料格式也必須一致。

 介面及展示

比如這裡郵箱地址我們隨便傳一個引數進去,他就會報錯。頁面上也是這樣的。

  當然這裡的提示我們是可以修改的。角色管理頁面的程式碼也是一樣的。

@{
    ViewBag.Title = "Index";
    Layout = "~/Areas/Common/Views/Layout/_Layout.cshtml";
}

<meta name="viewport" content="width=device-width" />
<script type="text/javascript">

    $(function () {



        //1.初始化Table
        var oTable = new TableInit();
        oTable.Init();

        //2.初始化Button的點選事件
        var oButtonInit = new ButtonInit();
        oButtonInit.Init();

    });
   var Url = "@Url.Action("GetUsersList")";
   // var Url = "/api/services/app/user/GetAll()";
    var TableInit = function () {
        var oTableInit = new Object();
        //初始化Table
        oTableInit.Init = function () {
            $('#tb_departments').bootstrapTable({
               // url: '../User/GetUsersList',
                url: Url,         //請求後臺的URL(*)
                method: 'get',                      //請求方式(*)
                toolbar: '#toolbar',                //工具按鈕用哪個容器
                striped: true,                      //是否顯示行間隔色
                cache: false,                       //是否使用快取,預設為true,所以一般情況下需要設定一下這個屬性(*)
                pagination: true,                   //是否顯示分頁(*)
                sortable: false,                     //是否啟用排序
                sortOrder: "asc",                   //排序方式
                queryParams: oTableInit.queryParams,//傳遞引數(*)
                sidePagination: "server",           //分頁方式:client客戶端分頁,server服務端分頁(*)
                pageNumber: 1,                       //初始化載入第一頁,預設第一頁
                pageSize: 2,                       //每頁的記錄行數(*)