欧美bbbwbbbw肥妇,免费乱码人妻系列日韩,一级黄片

Jquery Datatables的使用詳解

 更新時間:2020年01月30日 16:43:32   作者:秋寒嶼  
Datatables 是一款強大的Jquery表格處理插件,樣式方面可以兼容bootstrap3/4、JqueryUi等,也有默認的樣式可以選擇。使用Datatables可以很靈活的從服務端通過ajax更新表格數(shù)據(jù),實現(xiàn)排序、分頁等功能

參考:

Datatables中文網(wǎng)

Datatables官網(wǎng)

 Datatables 是一款強大的Jquery表格處理插件,樣式方面可以兼容bootstrap3/4、JqueryUi等,也有默認的樣式可以選擇。使用Datatables可以很靈活的從服務端通過ajax更新表格數(shù)據(jù),實現(xiàn)排序、分頁等功能。

一、安裝

登錄官網(wǎng)下載,可以看到有一個選擇的表單讓你自定義下載包的內容,可以選擇樣式、擴展組件、Jquery庫等,這個可以根據(jù)自己的需求下載,也可以先只下載Default的就可以了。

在你的項目中使用 DataTables,只需要引入三個文件即可,jQuery庫,一個DataTables的核心js文件和一個DataTables的css文件。有的時候還需要DataTables樣式的一些資源。

二、數(shù)據(jù)的綁定

如何將數(shù)據(jù)顯示到Datatables中呢,有三種方式:

1. Dom

如果在html中生命的table標簽下,tbody中有已經(jīng)編輯好的數(shù)據(jù)的話,會直接顯示出來。

<table id="table_id_example" class="display">
 <thead>
  <tr>
  <th>Column 1</th>
  <th>Column 2</th>
  </tr>
 </thead>
 <tbody>
  <tr>
  <td>Row 1 Data 1</td>
  <td>Row 1 Data 2</td>
  </tr>
  <tr>

2. JavaScript

可以在js中定義好數(shù)據(jù)源,然后在DT初始化的時候,通過data選項為表格配置數(shù)據(jù),數(shù)據(jù)源可以是數(shù)組、對象、實例三種形式。

(1)數(shù)組

var data = [
 [
  "Tiger Nixon",
  "System Architect",
  "Edinburgh",
  "5421",
  "2011/04/25",
  "$3,120"
 ],
 [
  "Garrett Winters",
  "Director",
  "Edinburgh",
  "8422",
  "2011/07/25",
  "$5,300"
 ]
 ];
 
 $('#example').DataTable( {
 data: data
 } );

(2)實例

 function Employee ( name, position, salary, office ) {
 this.name = name;
 this.position = position;
 this.salary = salary;
 this._office = office;
 
 this.office = function () {
  return this._office;
 }
 };
 
 $('#example').DataTable( {
 data: [
  new Employee( "Tiger Nixon", "System Architect", "$3,120", "Edinburgh" ),
  new Employee( "Garrett Winters", "Director", "$5,300", "Edinburgh" )
 ],
 columns: [
  { data: 'name' },
  { data: 'salary' },
  { data: 'office()' },
  { data: 'position' }
 ]
 } );

(3)對象

var data = [
 {
  "name": "Tiger Nixon",
  "position": "System Architect",
  "salary": "$3,120",
  "start_date": "2011/04/25",
  "office": "Edinburgh",
  "extn": "5421"
 },
 {
  "name": "Garrett Winters",
  "position": "Director",
  "salary": "$5,300",
  "start_date": "2011/07/25",
  "office": "Edinburgh",
  "extn": "8422"
 }
 ];
 //object可以如下初始化表格
 $('#example').DataTable( {
 data: data,
 //使用對象數(shù)組,一定要配置columns,告訴 DataTables 每列對應的屬性
 //data 這里是固定不變的,name,position,salary,office 為你數(shù)據(jù)里對應的屬性
 columns: [
  { data: 'name' },
  { data: 'position' },
  { data: 'salary' },
  { data: 'office' }
 ]
 } );

可以看到,在html中定義好一個id是example的table后,可以使用DT提供的選項進行初始化,data是數(shù)據(jù),可以將要展示的數(shù)據(jù)對象放到data選項后,然后通過columns選項為每一列的屬性進行定義,DT就會根據(jù)columns中定義的屬性找到對象中的成員進行綁定,如果是數(shù)組的話,會按照數(shù)組中定義的數(shù)據(jù)依次綁定到每一列上進行展示。

3. Ajax

前兩種數(shù)據(jù)源的處理模式都是客戶端處理,顯然這不能滿足大多數(shù)時候對于表格的要求。還好DT提供了一種可以通過Ajax與后端服務交互的方法,可以直接將后端提供的數(shù)據(jù)直接展示到表格上,分頁和排序也很方便。后面的篇幅會側重說DT如何通過Ajax如何與服務端完成數(shù)據(jù)交互。

三、通過選項完成一個服務端處理模式的Datatables

datatables中大量的選項可以用來定制你的表格展現(xiàn)給用戶。

舉個例子

datatables的配置是通過設置你定義的選項來完成的,如下:

$('#example').DataTable( {
 paging: false
} );

通過設置paging選項,禁止表格分頁(默認是打開的)

假設你要在表格里使用滾動,你需要加上scrollY選項:

$('#example').DataTable( {
 scrollY: 400
} );

當然你可以組合多個選項來初始化datatables,啟動滾動條,禁用分頁

$('#example').DataTable( {
 paging: false,
 scrollY: 400
} );

再比如在上面說到的data選項和columns選項都是一種初始化定制DT的方法。

可以看到,通過DataTable(object)函數(shù)可以進行DT的定制,object是一個對象,對象中的每個成員變量都應該是一個DT的選項。

因為我們在一個項目中可能會用到多個DT,那有些選項其實是通用的,舉個例子如果A、B的表格都允許排序并且允許檢索,正常會寫成:

$('#a).DataTable( {
 searching: true,
 ordering: true
} );

$('#b).DataTable( {
 searching: true,
 ordering: true
} );

當使用的表格多了后,后續(xù)維護會非常的麻煩,所以可以將通用的選項提取出來,然后再通用選項的基礎上定制每個DT。

function getCommonOptions()
{
 var options = new Object();
 options.searching = true;
 options.ordering = true;
 return options;
}


var aOptions = getCommonOptions();
aOptions.scrollY = true;
$('#a).DataTable(aOptions);

var bOptions = getCommonOptions();
bOptions .scrollY = false;
$('#b).DataTable(bOptions);

這樣,A和B都支持了檢索和排序,但是A支持垂直滾動,但是B不支持垂直滾動。

所以首先可以看下一個公用的通過Ajax獲取后端數(shù)據(jù)的DT是如何配置的。

/**通用列表**/
function createCommonTableOptions() {
 var oTemp = new Object;
 // 是否允許檢索
 oTemp.searching = true;
 // 是否允許排序
 oTemp.ordering = true;
 // 默認排序
 oTemp.order = [[1,'desc']];
 // 是否顯示情報 就是"當前顯示1/100記錄"這個信息
 oTemp.info = true;
 // 是否允許翻頁,設成false,翻頁按鈕不顯示
 oTemp.paging = true;
 // 水平滾動條
 oTemp.scrollX = false;
 // 垂直滾動條
 oTemp.scrollY = true;
 // 是否可以選擇每頁展示的Item數(shù)量
 oTemp.lengthChange = true;
 // 選擇每頁展示數(shù)量的選項
 oTemp.lengthMenu = [10, 25, 50, 75, 100];
 // 每頁展示數(shù)據(jù)條數(shù)默認值
 oTemp.pageLength = 10;
 //翻頁按鈕樣式
 // numbers:數(shù)字
 // simple:前一頁,后一頁
 // simple_numbers:前一頁,后一頁,數(shù)字
 // full:第一頁,前一頁,后一頁,最后頁
 //full_numbers:第一頁,前一頁,后一頁,最后頁,數(shù)字
 //first_last_numbers:第一頁,最后頁,數(shù)字
 oTemp.pagingType = "simple_numbers";
 // 行樣式應用 指定多個的話,第一行tr的class為strip1,第二行為strip2,第三行為strip3.
 // 第四行以后又開始從strip1循環(huán)。。。 如果想指定成斑馬條狀,這里的class必須指定為2個。
 oTemp.stripeClasses = ['line_1', 'line_2'];
 // 自動列寬
 oTemp.autoWidth = true;
 // 是否表示 "processing" 加載中的信息,這個信息可以修改
 oTemp.processing = true;
 // 每次創(chuàng)建是否銷毀以前的DataTable,默認false
 oTemp.destroy = false;
 // 控制表格各種信息的表示位置(比較復雜) 默認:lfrtip
 // 具體參考:https://datatables.net/reference/option/dom
 oTemp.dom = "lrtip";
 // language 用來定義展示信息的內容,如加載中顯示的提示、當前頁顯示多少條時的提示、翻頁按鈕上的文字等等
 oTemp.language = {
 "processing": "翻頁中。。。。",
 // 當前頁顯示多少條
 "lengthMenu": "當前顯示 _MENU_ 條記錄",
 // _START_(當前頁的第一條的序號) ,_END_(當前頁的最后一條的序號),_TOTAL_(篩選后的總件數(shù)),
 // _MAX_(總件數(shù)),_PAGE_(當前頁號),_PAGES_(總頁數(shù))
 "info": "當前第 _PAGE_ 頁, 共 _PAGES_ 頁, 共 _MAX_ 條數(shù)據(jù)",
 "infoEmpty": "0條數(shù)據(jù)",
 "infoFiltered": "",
 // 沒有數(shù)據(jù)的顯示(可選),如果沒指定,會用zeroRecords的內容
 "emptyTable": "沒有記錄",
 // 篩選后,沒有數(shù)據(jù)的表示信息,注意emptyTable優(yōu)先級更高
 "zeroRecords": "沒有符合條件的記錄",
 // 千分位的符號,只對顯示有效,默認就是"," 一般不要改寫
 //"thousands": "'",
 // 小數(shù)點位的符號,對輸入解析有影響,默認就是"." 一般不要改寫
 //"decimal": "-",
 // 翻頁按鈕文字控制
 "paginate": {
  "first": "第一頁",
  "last": "最后一頁",
  "next": "上一頁",
  "previous": "下一頁"
 },
 "loadingRecords": "正在加載中,請稍后。。。"
 };
 // 默認是false
 // 如果設為true,將只渲染當前也的html,速度會很快,但是通過API就訪問不到所有頁的數(shù)據(jù),有利有弊
 //"deferRender": false,
 // 服務器端處理方式
 oTemp.serverSide = true;
 return oTemp;
}

每行選項都加了注釋,如果希望了解更詳細,可以參考官方的用戶手冊。注意serverSide一定要設置為true。

一個項目中的多個表最大的不同是啥呢?首先,肯定是他們的列不同,如果列相同的話就成了相同的表格了。其次,因為列不同,所以需要綁定的數(shù)據(jù)以及數(shù)據(jù)的接口肯定也不同。

通過一個具體的例子看一下。我要實現(xiàn)一個功能,手機通過定時輪詢的方法查看服務端向他發(fā)送的命令(比如讓手機上報自己的定位、上傳自己的通話記錄等)。那這個服務端向手機發(fā)送的命令,我需要一個后臺管理平臺進行管理,其中一個表格就是要展示所有的命令,包括命令內容、創(chuàng)建時間和執(zhí)行時間等。

首先看下官方給出的前端通過Ajax向后端服務傳遞的入?yún)⒏袷剑?/p>

名稱 類型 描述
draw integerJS

繪制計數(shù)器。這個是用來確保Ajax從服務器返回的是對應的(Ajax是異步的,因此返回的順序是不確定的)。 要求在服務器接收到此參數(shù)后再返回(具體看 下面

start integerJS

第一條數(shù)據(jù)的起始位置,比如0代表第一條數(shù)據(jù)

length integerJS

告訴服務器每頁顯示的條數(shù),這個數(shù)字會等于返回的 data集合的記錄數(shù),可能會大于因為服務器可能沒有那么多數(shù)據(jù)。這個也可能是-1,代表需要返回全部數(shù)據(jù)(盡管這個和服務器處理的理念有點違背)

search[value] stringJS

全局的搜索條件,條件會應用到每一列( searchable需要設置為 true )

search[regex] booleanJS

如果為 true代表全局搜索的值是作為正則表達式處理,為 false則不是。 注意:通常在服務器模式下對于大數(shù)據(jù)不執(zhí)行這樣的正則表達式,但這都是自己決定的

order[i][column] integerJS

告訴后臺那些列是需要排序的。 i是一個數(shù)組索引,對應的是 columns配置的數(shù)組,從0開始

order[i][dir] stringJS

告訴后臺列排序的方式, desc 降序 asc升序

columns[i][data] stringJS

columns 綁定的數(shù)據(jù)源,由 columns.dataOption 定義。

columns[i][name] stringJS

columns 的名字,由 columns.nameOption 定義。

columns[i][searchable] booleanJS

標記列是否能被搜索,為true代表可以,否則不可以,這個是由 columns.searchableOption 控制

columns[i][orderable] booleanJS

標記列是否能排序,為 true代表可以,否則不可以,這個是由 columns.orderableOption 控制

columns[i][search][value] stringJS

標記具體列的搜索條件

columns[i][search][regex] booleanJS

特定列的搜索條件是否視為正則表達式, 如果為 true代表搜索的值是作為正則表達式處理,為 false則不是。 注意:通常在服務器模式下對于大數(shù)據(jù)不執(zhí)行這樣的正則表達式,但這都是自己決定的

這個param是由DT自己生成的,我們也可以自己增加一些我們想添加的入?yún)?shù)據(jù)。先看一下這個DT自己生成的參數(shù)在調試過程中抓取的結構:

 

感覺有點復雜,其實我們自己開發(fā)后端時候所需要的查詢條件并沒有那么復雜,所以在后端接受的時候可以簡化一下

package com.springapp.mvc.params;

/**
 * Created by qinyy on 2018/8/14.
 */
public class OperationQueryParam
{
 private int draw;
 // 分頁查詢起始下標
 private int start;
 // 分頁查詢偏移量--- 既每頁展示的數(shù)據(jù)數(shù)量
 private int offset;
 // 排序屬性
 private String sortPro;
 // 排序方式 0 asc 1 desc
 private int sortType;

 public String getSortPro()
 {
 return sortPro;
 }

 public void setSortPro(String sortPro)
 {
 this.sortPro = sortPro;
 }

 public int getSortType()
 {
 return sortType;
 }

 public void setSortType(int sortType)
 {
 this.sortType = sortType;
 }

 public int getStart()
 {
 return start;
 }

 public void setStart(int start)
 {
 this.start = start;
 }

 public int getOffset()
 {
 return offset;
 }

 public void setOffset(int offset)
 {
 this.offset = offset;
 }

 public int getDraw() {
 return draw;
 }

 public void setDraw(int draw) {
 this.draw = draw;
 }
}

我只接受這些字段就好了,就可以完成查詢了,draw這個字段其實就是一個標識,因為查詢是異步的,所以需要在服務端查詢好數(shù)據(jù)后把這個標識原封不動的返回給DT,使DT可以將請求和響應的處理對應起來。

我在服務端需要接受的東西有些是DT不會自動生成的,那么如何添加這些我們自己覺得有用的字段呢?一會兒說完出參格式的時候會把代碼貼出來進行說明。

再看一下官方給出的出參的說明

名稱 類型 描述
draw integerJS

必要。上面提到了,Datatables發(fā)送的draw是多少那么服務器就返回多少。 這里注意,作者出于安全的考慮,強烈要求把這個轉換為整形,即數(shù)字后再返回,而不是純粹的接受然后返回,這是 為了防止跨站腳本(XSS)攻擊。

recordsTotal integerJS

必要。即沒有過濾的記錄數(shù)(數(shù)據(jù)庫里總共記錄數(shù))

recordsFiltered integerJS

必要。過濾后的記錄數(shù)(如果有接收到前臺的過濾條件,則返回的是過濾后的記錄數(shù))

data arrayType

必要。表中中需要顯示的數(shù)據(jù)。這是一個對象數(shù)組,也可以只是數(shù)組,區(qū)別在于 純數(shù)組前臺就不需要用 columns綁定數(shù)據(jù),會自動按照順序去顯示 ,而對象數(shù)組則需要使用 columns綁定數(shù)據(jù)才能正常顯示。 注意這個 data的名稱可以由 ajaxOption ajax不定時一講 的 ajax.dataSrcOption ajax.dataSrc 1不定時一講 ajax.dataSrc 2不定時一講 控制

error stringJS

可選。你可以定義一個錯誤來描述服務器出了問題后的友好提示

除了上面的返回參數(shù)以外你還可以加入下面的參數(shù),來實現(xiàn)對表格數(shù)據(jù)的自動綁定

名稱 類型 描述
DT_RowId stringJS

自動綁定到 tr節(jié)點上

DT_RowClass stringJS

自動把這個類名添加到 tr

DT_RowData objectJS

使用 jQuery.data() 方法把數(shù)據(jù)綁定到row中,方便之后用來檢索(比如加入一個點擊事件)

DT_RowAttr objectJS

自動綁定數(shù)據(jù)到 tr上,使用 jQuery.attr() 方法,對象的鍵用作屬性,值用作屬性的值。注意這個 需要 Datatables 1.10.5+的版本才支持

其實除了data這個字段以外,其他的信息都是交給DT自己控制的,比如說recordsTotal,我們在后端查詢出所有記錄的數(shù)量后,把這個值置好,那么DT收到后就會在這里顯示出這個記錄的數(shù)量。然后我們可以截取data這個列表,進行進一步的綁定處理。

看下服務端定義的出參的格式:

public class CommonDatatableBean
{
 private int draw;
 private int recordsTotal;
 private int recordsFiltered;
 private List<Object> data;

 public int getDraw()
 {
 return draw;
 }

 public void setDraw(int draw)
 {
 this.draw = draw;
 }

 public int getRecordsTotal()
 {
 return recordsTotal;
 }

 public void setRecordsTotal(int recordsTotal)
 {
 this.recordsTotal = recordsTotal;
 }

 public int getRecordsFiltered()
 {
 return recordsFiltered;
 }

 public void setRecordsFiltered(int recordsFiltered)
 {
 this.recordsFiltered = recordsFiltered;
 }

 public List<Object> getData()
 {
 return data;
 }

 public void setData(List<Object> data)
 {
 this.data = data;
 }
}

可以看到這是一個通用的格式,所有的表格數(shù)據(jù)都可以封裝在這個對象中返回,區(qū)別就是data中Object類型不同而已。

入?yún)⒑统鰠⒏袷秸f完了,可以看一下DT如何設置ajax請求后端的數(shù)據(jù)

var operationTableOption = createCommonTableOptions();
 operationTableOption.ajax = {
 // url可以直接指定遠程的json文件,或是MVC的請求地址 /Controller/Action
 url: "/operation/query",
 type: 'POST',
 // 異步調用
 async:true,
 // 傳給服務器的數(shù)據(jù),可以添加我們自己的查詢參數(shù)
 data: function (param)
 {
  param.start = param.start;
  param.offset = param.length;
  switch (param.order[0].column)
  {
  case 1:
   param.sortPro = "createtime";
   break;
  case 2:
   param.sortPro = "excutetime";
   break;
  }

  if("asc" == param.order[0].dir)
  {
  param.sortType = 0;
  }
  else if("desc" == param.order[0].dir)
  {
  param.sortType = 1;
  }
  return param;
 },
 //用于處理服務器端返回的數(shù)據(jù)。 dataSrc是DataTable特有的
 dataFilter: function (myJson)
 {
  var result = JSON.parse(myJson);
  var retStr = JSON.stringify(result.data);
  return retStr;
 }
 };

首先,通過通用配置選項的函數(shù)獲取了一個通用選項的對象operationTableOption,然后為operationTableOption設置ajax選項。

首先設置請求地址,因為ajax正常情況下是不能跨域的,所以直線后面的path就可以,然后指定請求類型為POST。設置為異步請求。定義一個攔截請求的方法設置到data屬性中,這樣就可以完成自己請求的定制了。

這個請求主要就是根據(jù)要排序列的序號,給服務端傳遞排序字段的名稱以及排序方式,這樣服務端就可以不用了解DT入?yún)⒌臄?shù)據(jù)格式,而根據(jù)接口文檔開發(fā)就行了。

最后有個dataFilter屬性,這里要定義一個方法攔截服務端給前端返回的json數(shù)據(jù),將json中的data數(shù)據(jù)列表(上面說出參格式的時候提到過)給剝離出來并返回,這樣一會兒定義列數(shù)據(jù)綁定的時候就可以直接使用這個列表了。

ajax選項定義完畢,下一步要定義列相關的選項,定義列可以使用 columns 和 columnDefs這兩個選項都可以用來定義列,首先,這兩個選項中都要放入一個Definition Object的數(shù)組,區(qū)別就是columns需要對所有的列進行定義,也就是說這個表的每一個列都要和選項中放的DeinitionObject的一個子元素對應行程映射,columnDefs可以使用target來指定某一個定義對象應用到某一列,或者某一個定義對象是全局生效的,而且允許對同一個列進行多次定義。

為了使定義更清晰、更好理解、更方便維護,我一般都是使用columns進行定義。

operationTableOption.columns = [
 { "data": "operationid","orderable": false },
 { "data": "createtime" ,
  "orderable": true,
  "render": function ( data, type, row, meta )
  {
  if(data > 0)
   return $.myTime.UnixToDate(data,true)
  else
   return ""
  }
 },
 { "data": "excutetime" ,
  "orderable": true,
  "render": function ( data, type, row, meta )
  {
  if(data > 0)
   return $.myTime.UnixToDate(data,true)
  else
   return ""
  }},
 { "data": "needuploadlocation","orderable": false,
  "render": function ( data, type, row, meta )
  {
  if(data)
  {
   return "是";
  }
  else
  {
   return "否";
  }
  }
 },
 { "data": "recordsbegindate","orderable": false },
 { "data": "recordsenddate" ,"orderable": false},
 { "data": "uploadsoundsids","orderable": false },
 {"render":function ( data, type, row, meta )
 {

  var operationId = row.operationid;
  return "<button type=\"button\"οnclick=\"deleteOperation("+ operationId +"," +meta.row+ ")\">刪除</button>";
 }}
 ];

可以看到,將后端給前端返回的出參,使用屬性名依次綁定到列上,orderatble是說明這一列是否支持排序。render可以定義一個function也可以定義成其他屬性,是用來轉換數(shù)據(jù)的,比如返回的時間是一個時間戳,但是我要現(xiàn)實的是一個fomat的時間,就可以在render中轉換。還有最后一行,我想在最后一列添加一個刪除按鈕,那這個刪除按鈕就可在這里通過數(shù)據(jù)的唯一索引來動態(tài)生成。

看下官方對render中使用function定義的說明:

function render( data, type, row, meta )

Description:

If a function is given, it will be executed whenever DataTables needs to get the data for a cell in the column. Note that this function might be called multiple times, as DataTables will call it for the different data types that it needs - sorting, filtering and display.

Parameters:

Name Type Optional
1 data

Any

No

The data for the cell (based on columns.data)

2 type

string

No

The type call data requested. This is used for . This value will be one of:

See also the cell().render() method which can be used to execute the given rendering method at any arbitrary time.

3 row

Any

No

The full data source for the row (not based on columns.data)

4 meta

object

No

Since 1.10.1: An object that contains additional information about the cell being requested. This object contains the following properties:

  • row - The row index for the requested cell. See row().index().
  • col - The column index for the requested cell. See column().index().
  • settings - The DataTables.Settings object for the table in question. This can be used to obtain an API instance if required.

對ajax和columns都設置好以后,就可以將整個選項對象綁定到DT中了

$("table.operation-table").DataTable(operationTableOption);

四、服務端支持

@ResponseBody
 @RequestMapping(method = RequestMethod.POST,value = "query")
 public BaseResult queryOperation(OperationQueryParam param)
 {
 if(param == null)
  return null;
 BaseResult result = new BaseResult();
 CommonDatatableBean tableBean = new CommonDatatableBean();
 tableBean.setDraw(param.getDraw());
 int totalAmount = mOperationService.getOperationCount();
 tableBean.setRecordsTotal(totalAmount);
 tableBean.setRecordsFiltered(totalAmount);
 int limit = param.getStart();
 List<Object> list = new ArrayList<Object>();
 String sortType = param.getSortType() == 0?"ASC":"DESC";
 List<Operation> operations = mOperationService.queryOperationPaged(limit,param.getOffset(),param.getSortPro(),sortType);
 if(operations != null && operations.size() > 0)
 {
  for(Operation operation:operations)
  {
  list.add(operation);
  }
 }
 tableBean.setData(list);
 result.setData(tableBean);
 result.setResult(ResultCode.SUCCESS);
 return result;
 }

好了這篇文章就介紹到這了,更多內容可以查看腳本之家以前發(fā)布的文章。

相關文章

最新評論