Vue自訂分頁-以MVC C#實做
近期在學習並使用Vue.js,剛好有做資料分頁的需求,雖說網路上流傳著不少好用元件以及範例程式碼,但因為傻露的需求可能比較特別,老是找不到合意的,故最後就決定自己刻啦。
註:傻露想要的效果類似shield ui醬子
使用語言、資源及相關架構
Vue.js 2.4.2
jQuery 1.9.1
MVC 5(VS2013)
C#
Json.net(不知道第幾版但版號應該沒差)
bootstrap 3.3.7
awesome font 4.7
以下是正文XD
設有一群的資料要依分頁顯示,並依照按下分頁時才至資料庫取得該分頁的資料集合。
假設頁碼範圍1-4頁,分頁長像如下:
[|<][<][1][2][3][4][…][>][>|]
以下就上面所示撰寫範例。
後端
1.web.config初始化設定
在web.config內設定初始顯示的頁碼、每頁顯示的資料量、以及畫面上要顯示的頁碼範圍(即本例的4頁)
<configuration>
<appSettings>
<!--取得資料的起始頁數-->
<add key="INIPAGE" value="1"/>
<!--每頁顯示資料筆數-->
<add key="CNT_PAGEITEM" value="7"/>
<!--page顯示頁碼集合數-->
<add key="CNT_PAGEITEM_MAX" value="4"/>
</appSettings>
</configuration>
2.資料
這邊為了方便起見,不連接資料庫
使用資料來源: https://www.json-generator.com/api/json/get/cknklDscqG?indent=2
以下程式碼因篇幅關係故僅顯示部分資料
public class SimpleData
{
public string GetSimpleData()
{
return data;
}
private string data = @"[{
'index': 0,
'age': 56,
'name': 'Flowers Harmon',
'gender': 'male'
},
{
'index': 1,
'age': 60,
'name': 'Angie Matthews',
'gender': 'female'
},
{
'index': 2,
'age': 28,
'name': 'Gina Randolph',
'gender': 'female'
}]";
}
3.資料模型
定義顯示的資料model
public class ModelData
{
public int index { get; set; }
public string name { get; set; }
public int age { get; set; }
public string gender { get; set; }
public string TOTPAGE { get; set; }
public string PAGED { get; set; }
}
4.分頁資料模型
定義分頁的資料model
public class PageData
{
//pager model
//往前
public bool EnableLeft { get; set; }
//往後
public bool EnableRight { get; set; }
//省略符號(前)
public bool DisplayLeftEllipsesZone { get; set; }
//省略符號(後)
public bool DisplayRightEllipsesZone { get; set; }
//省略符號index(前)
public string LeftEllipsIndex { get; set; }
//省略符號index(後)
public string RightEllipsIndex { get; set; }
//顯示頁碼
public List<string> DisplayPageZone { get; set; }
//總頁數
public string TotalPage { get; set; }
//現在頁數
public string CurrentPage { get; set; }
public PageData()
{
this.EnableLeft = false;
this.EnableRight = false;
this.DisplayLeftEllipsesZone = false;
this.DisplayRightEllipsesZone = false;
this.LeftEllipsIndex = "1";
this.RightEllipsIndex = "1";
this.TotalPage = "1";
this.CurrentPage = "1";
this.DisplayPageZone = new List<string>();
}
}
5.分頁資料邏輯
這部分是參考網友的程式碼,再依據自己的需求調整
public class CommonTool
{
/// <summary>
/// 自訂分頁物件
/// </summary>
/// <param name="currPage">所在頁碼</param>
/// <param name="cntPage">顯示頁碼maxValue(每頁顯示資料量)</param>
/// <param name="totPage">資料總頁數</param>
/// <returns></returns>
public PageData GetDisplayPages(int currPage, int cntPage, int totPage)
{
PageData pgdata = new PageData();
int ppSize = totPage / cntPage;
if (totPage % cntPage > 0)
ppSize++;
int currppSize = 1;
if (currPage % cntPage != 0)
currppSize = (currPage / cntPage) + 1;
else
currppSize = (currPage / cntPage);
//判斷是否顯示省略符號
int pStart = 1;
if (currPage % cntPage != 0)
pStart = ((currPage / cntPage) * cntPage + 1);
else
pStart = ((currPage - 1) / cntPage) * cntPage+1;
int pEnd = pStart + (cntPage - 1);
if (pEnd > totPage)
pEnd = totPage;
if (currppSize > 1)
{
pgdata.DisplayLeftEllipsesZone = true;
pgdata.LeftEllipsIndex = (pStart - 1).ToString();
}
if (currppSize < ppSize)
{
pgdata.DisplayRightEllipsesZone = true;
pgdata.RightEllipsIndex = (pEnd + 1).ToString();
}
//畫面上顯示之頁碼
for (int i = pStart; i <= pEnd; i++)
{
pgdata.DisplayPageZone.Add(i.ToString());
}
//首頁末頁
if (currPage == 1)
{
if (totPage > 1)
pgdata.EnableRight = true;
}
else
{
if (currPage == totPage)
pgdata.EnableLeft = true;
else
{
pgdata.EnableLeft = true;
pgdata.EnableRight = true;
}
}
pgdata.TotalPage = totPage.ToString();
pgdata.CurrentPage = currPage.ToString();
return pgdata;
}
}
6.撰寫取資料、取得分頁相關設定值的Service
public class DataService
{
/// <summary>
/// 取得資料結果
/// </summary>
/// <param name="pagnum">頁碼</param>
/// <param name="pagedata">每頁資料量</param>
/// <returns></returns>
public dynamic GetResult(string pagnum, string pagedata)
{
List<ModelData> result = new List<ModelData>();
List<ModelData> data = JsonConvert.DeserializeObject<List<ModelData>>(new SimpleData().GetSimpleData());
//計算該筆資料所在頁數
int i = 0;
foreach (var t in data)
{
i++;
t.PAGED = Math.Ceiling(Convert.ToDouble(i) / Convert.ToDouble(pagedata)).ToString();
}
//計算總頁數
var totpage = Math.Ceiling(Convert.ToDouble(data.Count) / Convert.ToDouble(pagedata));
result = data.Where(t => t.PAGED == pagnum.ToString()).ToList();
result[0].TOTPAGE = totpage.ToString();
return result;
}
//取得分頁資料結果
public dynamic GetPager(string currpage, string countpage, string totpage)
{
CommonTool tool = new CommonTool();
return tool.GetDisplayPages(Convert.ToInt32(currpage), Convert.ToInt32(countpage), Convert.ToInt32(Convert.ToDouble(totpage)));
}
}
7.HomeController.cs
設定View初始值
public ActionResult Index()
{
SetInitial();
return View();
}
private void SetInitial()
{
ViewBag.Inipage = System.Configuration.ConfigurationManager.AppSettings["INIPAGE"].ToString();
ViewBag.Pageitem = System.Configuration.ConfigurationManager.AppSettings["CNT_PAGEITEM"].ToString();
ViewBag.Pagemax = System.Configuration.ConfigurationManager.AppSettings["CNT_PAGEITEM_MAX"].ToString();
}
再來增加撰寫取得資料並拋回前端的程式碼
DataService service = new DataService();
[HttpPost]
public ActionResult GetData(string pagnum, string pagedata)
{
string maxnum = System.Configuration.ConfigurationManager.AppSettings["CNT_PAGEITEM_MAX"].ToString();
//資料
var result = service.GetResult(pagnum, pagedata);
if (result != null)
{
//分頁資料
var PagerData = service.GetPager(pagnum, maxnum, result[0].TOTPAGE);
return Json(new { rtncode = "1", rtnmsg = "success", data = result, pager = PagerData });
}
else
return Json(new { rtncode = "0", rtnmsg = "failure" });
}
前端
8.Index.cshtml
@{
ViewBag.Title = "Index";
}
@Html.Hidden("getinipage", (object)ViewBag.Inipage)
@Html.Hidden("getpageitem", (object)ViewBag.Pageitem)
@Html.Hidden("getpagemax", (object)ViewBag.Pagemax)
<html>
<head>
<link rel="stylesheet" type="text/css" href="https://maxcdn.bootstrapcdn.com/font-awesome/4.7.0/css/font-awesome.min.css">
<link rel="stylesheet" href="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/css/bootstrap.min.css">
<link href="~/Content/styles.css" rel="stylesheet" />
</head>
<body>
<div id="app" v-cloak>
<table class="table table-condensed">
<tr>
<th>#</th>
<th>name</th>
<th>age</th>
<th>gender </th>
</tr>
<tr v-for="(r, index) in dataVal">
<td>{{ r.index+1 }}</td>
<td>{{ r.name }}</td>
<td>{{ r.age }}</td>
<td>{{ r.gender }}</td>
</tr>
</table>
<div class="page-footer">
<div class="container-fluid text-center py-3">
<div class="row">
@{ Html.RenderPartial("Partial_Page");}
</div>
</div>
</div>
</div>
<script src="http://code.jquery.com/jquery-1.9.1.js"></script>
<script src="https://cdnjs.cloudflare.com/ajax/libs/vue/2.4.2/vue.min.js"></script>
<script src="https://maxcdn.bootstrapcdn.com/bootstrap/3.3.7/js/bootstrap.min.js"></script>
<script src="~/Scripts/index.js"></script>
</body>
</html>
分頁Partial_Page.cshtml
<div class="pagination">
<ul class="pagination pagination-sm">
<li class="page-item" v-bind:class="[dataPage.EnableLeft ? '' : 'btndisabled']" >
<a class="page-link" href="#" v-on:click="setPage(1)" title="第一頁"><i class="fa fa-angle-double-left fa-lg"></i></a>
</li>
<li class="page-item" v-bind:class="[dataPage.EnableLeft ? '' : 'btndisabled']" >
<a class="page-link" href="#" v-on:click="setPage(parseInt(dataPage.CurrentPage)-1)" title="上一頁"><i class="fa fa-angle-left fa-lg" ></i></a>
</li>
<li class="page-item">
<a class="page-link" href="#" v-show="dataPage.DisplayLeftEllipsesZone" v-on:click="setPage(dataPage.LeftEllipsIndex)" title="更多...">...</a>
</li>
<li class="page-item" v-for="pageNum in dataPage.DisplayPageZone" v-bind:class="[(parseInt(pageNum) === parseInt(dataPage.CurrentPage)) ? 'active' : '']">
<a class="page-link" href="#" v-on:click="setPage(pageNum)" >{{ pageNum }}</a>
</li>
<li class="page-item">
<a class="page-link" href="#" v-show="dataPage.DisplayRightEllipsesZone" v-on:click="setPage(dataPage.RightEllipsIndex)" title="更多...">...</a>
</li>
<li class="page-item" v-bind:class="[dataPage.EnableRight ? '' : 'btndisabled']">
<a class="page-link" href="#" v-on:click="setPage(parseInt(dataPage.CurrentPage)+1)" title="下一頁"><i class="fa fa-angle-right fa-lg"></i></a>
</li>
<li class="page-item" v-bind:class="[dataPage.EnableRight ? '' : 'btndisabled']">
<a class="page-link" href="#" v-on:click="setPage(dataPage.TotalPage)" title="最末頁"><i class="fa fa-angle-double-right fa-lg" ></i></a>
</li>
</ul>
</div>
9.分頁css
body {
padding-top: 70px;
/*font-family*/
font-family: "Helvetica", "Arial","LiHei Pro","黑體-繁","微軟正黑體", sans-serif;
line-height: 2;
font-size: 15px;
font-weight: 300;
}
/*pagination style*/
/*控制首末按鈕disabled*/
.btndisabled a{
pointer-events: none;
cursor:not-allowed;
background-color:#fafafa !important;
color:#b3b6bc !important;
}
/*覆寫bootstrap*/
.pagination-sm > li > a, .pagination-sm > li > span {
font-size:15px !important;
line-height:1 !important;
}
10.Javascript(index.js)
先撰寫Ajax取得資料
//startPageIndex:頁數
//eachPageNum:每頁顯示資料筆數
function GetAll(startPageIndex, eachPageNum)
{
var serviceURL = '../Home/GetData';
$.ajax({
type: 'post',
contentType: "application/json; charset=utf-8",
url: serviceURL,
data: JSON.stringify({ 'pagnum': startPageIndex, 'pagedata': eachPageNum }),
dataType: "json",
cache: false,
success: function (Result) {
if (Result.rtncode === '1') {
app.dataVal = Result.data;
app.dataPage = Result.pager;
}
else {
app.dataVal = [];
app.dataPage = [];
}
},
error: function () { /*alert(data.d.rtnmsg);*/ }
});
}
Ajax取得資料後餵給Vue
$(document).ready(function () {
//取得後端設定的相關資料
var startPageIndex = $("#getinipage").val();
var eachPageNum = $("#getpageitem").val();
//取資料
GetAll(startPageIndex, eachPageNum);
});
var app =
new Vue({
el: '#app',
data: {
dataVal: [],
dataPage: []
},
methods: {
setPage: function (page) {
var eachPageNum = $("#getpageitem").val();
GetAll(page, eachPageNum);
}
}
});
這樣就完成分頁啦,後來因重複使用需要,傻露也改寫成component,下回有機會再分享囉~。