• <fieldset id="8imwq"><menu id="8imwq"></menu></fieldset>
  • <bdo id="8imwq"><input id="8imwq"></input></bdo>
    最新文章專題視頻專題問答1問答10問答100問答1000問答2000關(guān)鍵字專題1關(guān)鍵字專題50關(guān)鍵字專題500關(guān)鍵字專題1500TAG最新視頻文章推薦1 推薦3 推薦5 推薦7 推薦9 推薦11 推薦13 推薦15 推薦17 推薦19 推薦21 推薦23 推薦25 推薦27 推薦29 推薦31 推薦33 推薦35 推薦37視頻文章20視頻文章30視頻文章40視頻文章50視頻文章60 視頻文章70視頻文章80視頻文章90視頻文章100視頻文章120視頻文章140 視頻2關(guān)鍵字專題關(guān)鍵字專題tag2tag3文章專題文章專題2文章索引1文章索引2文章索引3文章索引4文章索引5123456789101112131415文章專題3
    問答文章1 問答文章501 問答文章1001 問答文章1501 問答文章2001 問答文章2501 問答文章3001 問答文章3501 問答文章4001 問答文章4501 問答文章5001 問答文章5501 問答文章6001 問答文章6501 問答文章7001 問答文章7501 問答文章8001 問答文章8501 問答文章9001 問答文章9501
    當(dāng)前位置: 首頁 - 科技 - 知識百科 - 正文

    在ABP框架中使用BootstrapTable組件的方法

    來源:懂視網(wǎng) 責(zé)編:小采 時(shí)間:2020-11-27 22:33:28
    文檔

    在ABP框架中使用BootstrapTable組件的方法

    在ABP框架中使用BootstrapTable組件的方法:一、關(guān)于ABP ABP是ASP.NET Boilerplate Project (ASP.NET樣板項(xiàng)目)的簡稱,它是一個(gè)成熟的開源框架,基于DDD+Repository模式,自帶Zero權(quán)限和認(rèn)證模塊,避免了從零開始搭建框架的煩惱。關(guān)于ABP的框架優(yōu)勢就此打住,因?yàn)檫@樣說下去要說三天三夜,脫離文
    推薦度:
    導(dǎo)讀在ABP框架中使用BootstrapTable組件的方法:一、關(guān)于ABP ABP是ASP.NET Boilerplate Project (ASP.NET樣板項(xiàng)目)的簡稱,它是一個(gè)成熟的開源框架,基于DDD+Repository模式,自帶Zero權(quán)限和認(rèn)證模塊,避免了從零開始搭建框架的煩惱。關(guān)于ABP的框架優(yōu)勢就此打住,因?yàn)檫@樣說下去要說三天三夜,脫離文

    一、關(guān)于ABP

    ABP是“ASP.NET Boilerplate Project (ASP.NET樣板項(xiàng)目)”的簡稱,它是一個(gè)成熟的開源框架,基于DDD+Repository模式,自帶Zero權(quán)限和認(rèn)證模塊,避免了從零開始搭建框架的煩惱。關(guān)于ABP的框架優(yōu)勢就此打住,因?yàn)檫@樣說下去要說三天三夜,脫離文本主題。

    關(guān)于ABP的入門,博主不想說太多,園子里面tkb至簡和陽光銘睿有很多入門級的文章,有興趣的可以了解下,還是給出它的官網(wǎng)和開源地址。

    ABP官方網(wǎng)站:http://www.aspnetboilerplate.com

    ABP開源項(xiàng)目:https://github.com/aspnetboilerplate

    PS:如果你不愿意去看它的源碼,可以直接查看ABP官網(wǎng)上面的演示地址:https://aspnetzero.com/?ref=abptmplpage

    點(diǎn)擊CREATE MY DEMO按鈕,系統(tǒng)會自動為你生成演示地址

    進(jìn)入對應(yīng)的Demo URL

    使用演示的用戶名和密碼登陸進(jìn)去

    可以看到Zero模塊的實(shí)現(xiàn)效果。

    二、jTable在ABP中的運(yùn)用

    如果你下載ABP的源碼,并且選擇的是混合開發(fā)模式(ABP提供了兩種開發(fā)模式,一種是基于MVVM的Angular.js的模式;另一種就是MVC+jQuery的混合開發(fā)模式),如下圖:

    當(dāng)你Down下來源碼之后你就會發(fā)現(xiàn),ABP的源碼里面的UI部分的表格都是使用jTable去實(shí)現(xiàn)的。為什么會用jTable?原因很簡單,jTable是ABP的作者kalkan寫的一款開源插件,自己寫的肯定用自己的東西嘍。下面jTable的效果來一發(fā)。

    來一個(gè)jtable的父子表:

    如果是不帶父子表的簡單表格,其實(shí)jTable的效果其實(shí)還行,可是加上一些復(fù)雜的功能之后,那一片片藍(lán)色的區(qū)域不忍直視,并且jTable的api還有待完善,很多需要的功能都需要自己去實(shí)現(xiàn),于是就接到了將所有的表格組件換成BootstrapTable的需求,才有了今天的主題:在ABP中封裝BootstrapTable。

    三、Bootstrap Table在ABP中的封裝

    接到需求,博主各種百度、各種谷歌,都找不到Bootstrap Table組件在ABP中的封裝,有的只是在ABP的項(xiàng)目里面簡單的用傳統(tǒng)的方式去初始化組件,這并不是博主想要的。說到這里不得不說一下,如果你使用ABP開發(fā)的過程中遇到一些難題,你會發(fā)現(xiàn)很難從百度里面搜索到相關(guān)答案,谷歌里面有時(shí)能找到,但大部分都是英文社區(qū),所以如果你英文較弱,在查找資料上面會很吃虧,有時(shí)一個(gè)簡單的配置問題需要折騰很久。

    1、jTable在ABP項(xiàng)目里面的初始化

    首先來看看jTable在一般的ABP項(xiàng)目里面是如何初始化的。比如我們在Application里面有一個(gè)如下的接口和實(shí)現(xiàn)

     public interface IRequisitionAppService : IApplicationService
     {
     Task<PagedResultDto<RequisitionListDto>> GetRequisitionListAsync(GetRequisitionListInput input);
     }
      [AbpAuthorize(OrderAppPermissions.Pages_Order_Requisition)]
     public class RequisitionAppService : AbpZeroTemplateAppServiceBase, IRequisitionAppService
     {
     private readonly IRepository<Requisition, long> _requisitionRepository;
     public RequisitionAppService(IRepository<Requisition, long> requisitionRepository)
     {
     _requisitionRepository = requisitionRepository;
     }
         public async Task<PagedResultDto<RequisitionListDto>> GetRequisitionListAsync(GetRequisitionListInput input)
     {
     var query = _requisitionRepository.GetAll()
     .WhereIf(input.Status != null, w => (int)w.Status == input.Status.Value)
     .WhereIf(
     !input.Filter.IsNullOrWhiteSpace(),
     u =>
     u.No.Contains(input.Filter) ||
     u.Remark.Contains(input.Filter)
     );
     var count = await query.CountAsync();
     var list = await query
     .OrderBy(input.Sorting)
     .PageBy(input)
     .ToListAsync();
     var dtos = list.MapTo<List<RequisitionListDto>>();
     return new PagedResultDto<RequisitionListDto>(
     count,
     dtos
     );
     }
     }

    然后我們前端有一個(gè)頁面的列表數(shù)據(jù)從這個(gè)接口GetRequisitionListAsync()獲取

    <div class="portlet-body">
     <div id="dataListTable"></div>
    </div>
    (function () {
     $(function () {
     var _$dataListTable = $('#dataListTable');
     var _service = abp.services.app.requisition;
     _$dataListTable.jtable({
     paging: true,
     sorting: true,
     selecting: true,
     actions: {
     listAction: {
     method: _service.getRequisitionListAsync
     }
     },
     fields: {
     id: {
     key: true,
     list: false
     },
     details: {
     width: '1%',
     sorting: false,
     edit: false,
     create: false,
     listClass: 'child-opener-image-column',
     display: function (detailData) {
     var $img = $('<img class="child-opener-image" src="https://www.gxlcms.com/Common/Images/list_metro.png" title="申購明細(xì)" />');
     $img.click(function () {
     _$dataListTable.jtable('openChildTable',
     $img.closest('tr'),
     {
     title: "申購明細(xì)",
     showCloseButton: true,
     actions: {
     listAction: {
     method: _service.getRequisitionDetailListByIdAsync
     }
     },
     fields: {
     materialClassParentNameAndName: {
     title: app.localize('MaterialClassName'),
     width: '8%'
     },
     materialInfoTypeNo: {
     title: app.localize('TypeNo'),
     width: '5%'
     },
     materialInfoLengthDisplayName: {
     title: app.localize('LengthDisplayName'),
     width: '3%'
     },
     materialInfoWeight: {
     title: app.localize('Weight'),
     width: '5%',
     display: function (data) {
     return data.record.materialInfoMinWeight + '-' + data.record.materialInfoMaxWeight;
     }
     },
     materialInfoMouldTypeDisplayName: {
     title: app.localize('MouldTypeDisplayName'),
     width: '6%'
     },
     materialInfoProductionRemark: {
     title: app.localize('ProductionRemark'),
     width: '8%'
     },
     materialInfoBundleCountDisplayName: {
     title: app.localize('BundleCountDisplayName'),
     width: '3%'
     },
     materialInfoUnitDisplayName: {
     title: app.localize('UnitDisplayName'),
     width: '3%'
     },
     materialInfoProcessCost: {
     title: app.localize('ProcessCost'),
     width: '6%'
     },
     materialInfoProductRemark: {
     title: app.localize('ProductRemark'),
     width: '6%'
     },
     materialInfoRemark: {
     title: app.localize('Remark'),
     width: '6%'
     },
     count: {
     title: app.localize('申購數(shù)量'),
     width: '6%'
     },
     remark: {
     title: app.localize('申購備注'),
     width: '6%'
     }
     }
     }, function (data) {
     data.childTable.jtable('load',
     { requisitionId: detailData.record.id }
     );
     });
     });
     return $img;
     }
     },
     no: {
     title: "申購單號",
     width: '20%'
     },
     creatorUserName: {
     title: "申購人",
     width: '20%'
     },
     creationTime: {
     title: "申購時(shí)間",
     width: '10%',
     display: function (data) {
     return moment(data.record.creationTime).format('YYYY-MM-DD HH:mm:ss');
     }
     },
     sumCount: {
     title: "總數(shù)",
     width: '10%'
     },
     status: {
     title: "狀態(tài)",
     width: '20%',
     display: function (data) {
     if (data.record.status === app.order.requisitionAuditStatus.audit)
     return '<span class="label label-info">' + app.localize('Autdit') + '</span>'
     else if (data.record.status === app.order.requisitionAuditStatus.auditPass)
     return '<span class="label label-success">' + app.localize('Pass') + '</span>'
     else if (data.record.status === app.order.requisitionAuditStatus.auditReject)
     return '<span class="label label-danger">' + app.localize('Reject') + '</span>'
     else if (data.record.status === app.order.requisitionAuditStatus.delete)
     return '<span class="label label-danger">' + app.localize('Abandon') + '</span>'
     else
     return '<span class="label label-danger">' + app.localize('Unknown') + '</span>'
     }
     }
     }
     });
     });
    })();

    得到如下效果:

    代碼釋疑:

    (1) var _service = abp.services.app.requisition; 這一句聲明當(dāng)前頁面需要使用哪個(gè)服務(wù)。

    (2)  _service.getRequisitionListAsync 這一句對應(yīng)的是服務(wù)調(diào)用的方法,你會發(fā)現(xiàn)在后臺方法名是GetRequisitionListAsync(),而在js里面卻變成了getRequisitionListAsync(),我們暫且稱之為“潛規(guī)則”。

    2、bootstrapTable在ABP項(xiàng)目里面的封裝

    通過上述代碼你會發(fā)現(xiàn),ABP在application層里面定義的方法,最終會生成某一些js對應(yīng)的function,這里難點(diǎn)來了。我們找遍了bootstrapTable組件的api,都沒有通過某一個(gè)function去獲取數(shù)據(jù)的啊。這可如何是好?為這個(gè)問題,博主折騰了兩天。最開始博主想,function最終還不是要換成http請求的,我們只要拿到http請求的url,然后將function轉(zhuǎn)換為url不就行了么:

    我們使用bootstrapTable組件初始化的時(shí)候聲明  {url:'/api/services/app/requisition/GetRequisitionListAsync'}  這樣不就行了么?呵呵,經(jīng)過測試,這樣確實(shí)能正確取到數(shù)據(jù)。但是不夠理想,因?yàn)檫@前面的前綴是ABP給我們生成的,是否會變化我們尚且不說,給每一個(gè)url加上這么一長串著實(shí)看著很不爽,于是進(jìn)一步想,是否我們的bootstrapTable也可以使用function去初始化呢,組件沒有,難道我們就不能給他擴(kuò)展一個(gè)嗎?我們不用url獲取數(shù)據(jù),通過調(diào)用這個(gè)function取到數(shù)據(jù),然后將數(shù)據(jù)渲染到組件不就行了。思路有了,那么這里有兩個(gè)難題:一是如何將原來url的方式變成這里的調(diào)用function的方式呢?二是參數(shù)的封裝。經(jīng)過查看組件的源碼發(fā)現(xiàn),如果是服務(wù)端分頁,組件最終是進(jìn)入到initServer()這個(gè)方法去獲取數(shù)據(jù),然后渲染到頁面上面的,組件原始的initServer()方法如下:

    BootstrapTable.prototype.initServer = function (silent, query) {
     var that = this,
     data = {},
     params = {
     pageSize: this.options.pageSize === this.options.formatAllRows() ?
     this.options.totalRows : this.options.pageSize,
     pageNumber: this.options.pageNumber,
     searchText: this.searchText,
     sortName: this.options.sortName,
     sortOrder: this.options.sortOrder
     },
     request;
     if (!this.options.url && !this.options.ajax) {
     return;
     }
     if (this.options.queryParamsType === 'limit') {
     params = {
     search: params.searchText,
     sort: params.sortName,
     order: params.sortOrder
     };
     if (this.options.pagination) {
     params.limit = this.options.pageSize === this.options.formatAllRows() ?
     this.options.totalRows : this.options.pageSize;
     params.offset = this.options.pageSize === this.options.formatAllRows() ?
    : this.options.pageSize * (this.options.pageNumber - 1);
     }
     }
     if (!($.isEmptyObject(this.filterColumnsPartial))) {
     params['filter'] = JSON.stringify(this.filterColumnsPartial, null);
     }
     data = calculateObjectValue(this.options, this.options.queryParams, [params], data);
     $.extend(data, query || {});
     // false to stop request
     if (data === false) {
     return;
     }
     if (!silent) {
     this.$tableLoading.show();
     }
     request = $.extend({}, calculateObjectValue(null, this.options.ajaxOptions), {
     type: this.options.method,
     url: this.options.url,
     data: this.options.contentType === 'application/json' && this.options.method === 'post' ?
     JSON.stringify(data) : data,
     cache: this.options.cache,
     contentType: this.options.contentType,
     dataType: this.options.dataType,
     success: function (res) {
     res = calculateObjectValue(that.options, that.options.responseHandler, [res], res);
     that.load(res);
     that.trigger('load-success', res);
     },
     error: function (res) {
     that.trigger('load-error', res.status, res);
     },
     complete: function () {
     if (!silent) {
     that.$tableLoading.hide();
     }
     }
     });
     if (this.options.ajax) {
     calculateObjectValue(this, this.options.ajax, [request], null);
     } else {
     $.ajax(request);
     }
     };

    代碼不難讀懂,解析參數(shù),整合參數(shù),得到參數(shù),發(fā)送ajax請求,在success事件里面將得到的數(shù)據(jù)渲染到界面。讀懂了這段代碼,我們再來封裝function就容易多了。

    最終我們封裝的代碼如下:

    (function ($) {
     'use strict';
     //debugger;
     //通過構(gòu)造函數(shù)獲取到bootstrapTable里面的初始化方法
     var BootstrapTable = $.fn.bootstrapTable.Constructor,
     _initData = BootstrapTable.prototype.initData,
     _initPagination = BootstrapTable.prototype.initPagination,
     _initBody = BootstrapTable.prototype.initBody,
     _initServer = BootstrapTable.prototype.initServer,
     _initContainer = BootstrapTable.prototype.initContainer;
     //重寫
     BootstrapTable.prototype.initData = function () {
     _initData.apply(this, Array.prototype.slice.apply(arguments));
     };
     BootstrapTable.prototype.initPagination = function () {
     _initPagination.apply(this, Array.prototype.slice.apply(arguments));
     };
     BootstrapTable.prototype.initBody = function (fixedScroll) {
     _initBody.apply(this, Array.prototype.slice.apply(arguments));
     };
     BootstrapTable.prototype.initServer = function (silent, query) {
     //構(gòu)造自定義參數(shù)
     for (var key in this.options.methodParams) {
     $.fn.bootstrapTable.defaults.methodParams[key] = this.options.methodParams[key];
     }
     //如果傳了url,則走原來的邏輯
     if (this.options.url) {
     _initServer.apply(this, Array.prototype.slice.apply(arguments));
     return;
     }
     //如果定義了abpMethod,則走abpMethod的邏輯
     if (!this.options.abpMethod) {
     return;
     }
     var that = this,
     data = {},
     params = {
     pageSize: this.options.pageSize === this.options.formatAllRows() ?
     this.options.totalRows : this.options.pageSize,
     pageNumber: this.options.pageNumber,
     searchText: this.searchText,
     sortName: this.options.sortName,
     sortOrder: this.options.sortOrder
     },
     request;
     //debugger;
     if (this.options.queryParamsType === 'limit') {
     params = {
     search: params.searchText,
     sort: params.sortName,
     order: params.sortOrder
     };
     if (this.options.pagination) {
     params.limit = this.options.pageSize === this.options.formatAllRows() ?
     this.options.totalRows : this.options.pageSize;
     params.offset = this.options.pageSize === this.options.formatAllRows() ?
     0 : this.options.pageSize * (this.options.pageNumber - 1);
     }
     }
     if (!($.isEmptyObject(this.filterColumnsPartial))) {
     params['filter'] = JSON.stringify(this.filterColumnsPartial, null);
     }
     data = $.fn.bootstrapTable.utils.calculateObjectValue(this.options, this.options.queryParams, [params], data);
     $.extend(data, query || {});
     // false to stop request
     if (data === false) {
     return;
     }
     if (!silent) {
     this.$tableLoading.show();
     }
     this.options.abpMethod(data).done(function (result) {
     result = $.fn.bootstrapTable.utils.calculateObjectValue(that.options, that.options.responseHandler, [result], result);
     that.load(result);
     that.trigger('load-success', result);
     });
     request = $.extend({}, $.fn.bootstrapTable.utils.calculateObjectValue(null, this.options.ajaxOptions), {
     type: this.options.method,
     url: this.options.url,
     data: this.options.contentType === 'application/json' && this.options.method === 'post' ?
     JSON.stringify(data) : data,
     cache: this.options.cache,
     contentType: this.options.contentType,
     dataType: this.options.dataType,
     success: function (res) {
     debugger;
     res = $.fn.bootstrapTable.utils.calculateObjectValue(that.options, that.options.responseHandler, [res], res);
     that.load(res);
     that.trigger('load-success', res);
     },
     error: function (res) {
     that.trigger('load-error', res.status, res);
     },
     complete: function () {
     if (!silent) {
     that.$tableLoading.hide();
     }
     }
     });
     if (this.options.ajax) {
     $.fn.bootstrapTable.utils.calculateObjectValue(this, this.options.ajax, [request], null);
     } else {
     $.ajax(request);
     }
     }
     BootstrapTable.prototype.initContainer = function () {
     _initContainer.apply(this, Array.prototype.slice.apply(arguments));
     };
     abp.bootstrapTableDefaults = {
     striped: false,
     classes: 'table table-striped table-bordered table-advance table-hover',
     pagination: true,
     cache: false,
     sidePagination: 'server',
     uniqueId: 'id',
     showRefresh: false,
     search: false,
     method: 'post',
     //toolbar: '#toolbar',
     pageSize: 10,
     paginationPreText: '上一頁',
     paginationNextText: '下一頁',
     queryParams: function (param) {
     //$.fn.bootstrapTable.defaults.methodParams.propertyIsEnumerable()
     var abpParam = {
     Sorting: param.sort,
     filter: param.search,
     skipCount: param.offset,
     maxResultCount: param.limit
     };
     for (var key in $.fn.bootstrapTable.defaults.methodParams) {
     abpParam[key] = $.fn.bootstrapTable.defaults.methodParams[key];
     }
     return abpParam;
     },
     responseHandler: function (res) {
     if (res.totalCount)
     return { total: res.totalCount, rows: res.items };
     else
     return { total: res.result.totalCount, rows: res.result.items };
     },
     methodParams: {},
     abpMethod: function () { }
     };
     $.extend($.fn.bootstrapTable.defaults, abp.bootstrapTableDefaults);
    })(jQuery);

    代碼釋疑:增加兩個(gè)參數(shù) methodParams: {},abpMethod: function () { } 來獲取abp的function和參數(shù),然后獲取數(shù)據(jù)的時(shí)候如果定義了abpMethod,則通過function獲取數(shù)據(jù),否則還是走原來的邏輯。

    然后我們調(diào)用就簡單了

    //選取界面上要先數(shù)據(jù)的表格
     var _$SendOrdersTable = $('#SendOrdersTable');
     //獲取服務(wù)層方法
     var _SendOrderService = abp.services.app.sendOrder;
     _$SendOrdersTable.bootstrapTable({
     abpMethod: _SendOrderService.getSendOrderListAsync,
     detailView: true,
     onExpandRow: function (index, row, $detail) {
     var cur_table = $detail.html('<table></table>').find('table');
     $(cur_table).bootstrapTable({
     showRefresh: false,
     search: false,
     pagination: false,
     abpMethod: _SendOrderService.getSendOrderDetailListAsync,
     methodParams: { SendOrderId: row.id },
     columns: [
     {
     field: 'materialClassName',
     title: app.localize('MaterialClassName'),
     width: '8%'
     },
     {
     field: 'typeNo',
     title: app.localize('TypeNo'),
     width: '8%'
     }
     ]
     });
     },
     columns: [{
     field: 'no',
     title: app.localize('SendOrderNO'),
     align: 'center'
     },
     {
     field: 'supplierName',
     title: app.localize('SupplierName'),
     align: 'center'
     },
     {
     title: app.localize('SendOrderTime'),
     align: 'center',
     field: 'createdDate',
     formatter: function (data) {
     return moment(data).format('YYYY-MM-DD HH:mm:ss');
     }
     },
     {
     field: 'status',
     align: 'center',
     title: app.localize('SendOrderStatus'),
     formatter: function (data) {
     var value = "";
     if (data == 1) {
     value = '<span class="label label-info">' + app.localize('Autdit') + '</span>';
     }
     else if (data == 2) {
     value = '<span class="label label-success">' + app.localize('Pass') + '</span>';
     }
     else if (data == 3) {
     value = '<span class="label label-default">' + app.localize('Reject') + '</span>';
     }
     else
     value = '<span class="label label-default">' + app.localize('Abandon') + '</span>';
     return value;
     }
     },
     {
     field: 'createName',
     align: 'center',
     title: app.localize('SendOrderCreator'),
     },
     {
     field: 'sumCount',
     align: 'center',
     title: app.localize('SendOrderTotalCount'),
     },
     ]
     });

    得到如下效果

    總結(jié)

    以上所述是小編給大家介紹的在ABP框架中使用BootstrapTable組件的方法,希望對大家有所幫助,如果大家有任何疑問請給我留言,小編會及時(shí)回復(fù)大家的。在此也非常感謝大家對腳本之家網(wǎng)站的支持!

    聲明:本網(wǎng)頁內(nèi)容旨在傳播知識,若有侵權(quán)等問題請及時(shí)與本網(wǎng)聯(lián)系,我們將在第一時(shí)間刪除處理。TEL:177 7030 7066 E-MAIL:11247931@qq.com

    文檔

    在ABP框架中使用BootstrapTable組件的方法

    在ABP框架中使用BootstrapTable組件的方法:一、關(guān)于ABP ABP是ASP.NET Boilerplate Project (ASP.NET樣板項(xiàng)目)的簡稱,它是一個(gè)成熟的開源框架,基于DDD+Repository模式,自帶Zero權(quán)限和認(rèn)證模塊,避免了從零開始搭建框架的煩惱。關(guān)于ABP的框架優(yōu)勢就此打住,因?yàn)檫@樣說下去要說三天三夜,脫離文
    推薦度:
    標(biāo)簽: 組件 table 框架
    • 熱門焦點(diǎn)

    最新推薦

    猜你喜歡

    熱門推薦

    專題
    Top
    主站蜘蛛池模板: 欧美精品国产一区二区| 精品久久人人爽天天玩人人妻 | 国产精品久久久亚洲| 6一12呦女精品| 无码精品第一页| 国产精品福利电影一区二区三区四区欧美白嫩精品 | 国内精品久久久久伊人av| 久久99精品久久久久久不卡| 精品国产_亚洲人成在线高清| 国产精品爽爽va在线观看网站| 亚洲精品高清久久| 国产精品国产三级国产av品爱网| 青青草国产精品欧美成人| 久久久精品人妻一区二区三区四| 国产成人精品久久免费动漫| 亚洲精品狼友在线播放| 久久精品这里只有精99品| 2021年精品国产福利在线| 99热国内精品| CAOPORM国产精品视频免费| 免费视频精品一区二区三区| 久热这里只有精品12| 欧美成人精品第一区二区| 国产精品99爱免费视频| 亚洲精品国产成人专区| 久久亚洲国产欧洲精品一| 国产精品日韩欧美制服| 国产精品你懂的| 91麻豆精品国产| 99re热这里只有精品视频中文字幕| 国产精品欧美久久久久无广告 | 婷婷久久精品国产| 国产精品1024视频| 国产高清国内精品福利99久久| 自拍中文精品无码| 亚洲国产精品专区在线观看| 久久久不卡国产精品一区二区| 国产suv精品一区二区33| 久久精品亚洲中文字幕无码麻豆| 91嫩草亚洲精品| 国产精品国产高清国产专区|