温馨提示×

温馨提示×

您好,登录后才能下订单哦!

密码登录×
登录注册×
其他方式登录
点击 登录注册 即表示同意《亿速云用户服务条款》

如何在ABP框架中使用BootstrapTable组件

发布时间:2021-04-13 16:55:21 来源:亿速云 阅读:220 作者:Leah 栏目:web开发

本篇文章为大家展示了如何在ABP框架中使用BootstrapTable组件,内容简明扼要并且容易理解,绝对能使你眼前一亮,通过这篇文章的详细介绍希望你能有所收获。

一、关于ABP

ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称,它是一个成熟的开源框架,基于DDD+Repository模式,自带Zero权限和认证模块,避免了从零开始搭建框架的烦恼。关于ABP的框架优势就此打住,因为这样说下去要说三天三夜,脱离文本主题。

如何在ABP框架中使用BootstrapTable组件

点击CREATE MY DEMO按钮,系统会自动为你生成演示地址

如何在ABP框架中使用BootstrapTable组件

进入对应的Demo URL

如何在ABP框架中使用BootstrapTable组件

使用演示的用户名和密码登陆进去

如何在ABP框架中使用BootstrapTable组件

可以看到Zero模块的实现效果。

二、jTable在ABP中的运用

如果你下载ABP的源码,并且选择的是混合开发模式(ABP提供了两种开发模式,一种是基于MVVM的Angular.js的模式;另一种就是MVC+jQuery的混合开发模式),如下图:

如何在ABP框架中使用BootstrapTable组件

当你Down下来源码之后你就会发现,ABP的源码里面的UI部分的表格都是使用jTable去实现的。为什么会用jTable?原因很简单,jTable是ABP的作者kalkan写的一款开源插件,自己写的肯定用自己的东西喽。下面jTable的效果来一发。

如何在ABP框架中使用BootstrapTable组件

如何在ABP框架中使用BootstrapTable组件

来一个jtable的父子表:

如何在ABP框架中使用BootstrapTable组件

如果是不带父子表的简单表格,其实jTable的效果其实还行,可是加上一些复杂的功能之后,那一片片蓝色的区域不忍直视,并且jTable的api还有待完善,很多需要的功能都需要自己去实现,于是就接到了将所有的表格组件换成BootstrapTable的需求,才有了今天的主题:在ABP中封装BootstrapTable。

三、Bootstrap Table在ABP中的封装

接到需求,博主各种百度、各种谷歌,都找不到Bootstrap Table组件在ABP中的封装,有的只是在ABP的项目里面简单的用传统的方式去初始化组件,这并不是博主想要的。说到这里不得不说一下,如果你使用ABP开发的过程中遇到一些难题,你会发现很难从百度里面搜索到相关答案,谷歌里面有时能找到,但大部分都是英文社区,所以如果你英文较弱,在查找资料上面会很吃亏,有时一个简单的配置问题需要折腾很久。

1、jTable在ABP项目里面的初始化

首先来看看jTable在一般的ABP项目里面是如何初始化的。比如我们在Application里面有一个如下的接口和实现

如何在ABP框架中使用BootstrapTable组件

 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     );   }  }

然后我们前端有一个页面的列表数据从这个接口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="/Common/Images/list_metro.png" title="申购明细" />');       $img.click(function () {        _$dataListTable.jtable('openChildTable',         $img.closest('tr'),         {          title: "申购明细",          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('申购数量'),            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: "申购时间",      width: '10%',      display: function (data) {       return moment(data.record.creationTime).format('YYYY-MM-DD HH:mm:ss');      }     },     sumCount: {      title: "总数",      width: '10%'     },     status: {      title: "状态",      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>'      }     }    }   });  }); })();

得到如下效果:

如何在ABP框架中使用BootstrapTable组件

代码释疑:

(1) var _service = abp.services.app.requisition; 这一句声明当前页面需要使用哪个服务。

(2)  _service.getRequisitionListAsync 这一句对应的是服务调用的方法,你会发现在后台方法名是GetRequisitionListAsync(),而在js里面却变成了getRequisitionListAsync(),我们暂且称之为“潜规则”。

2、bootstrapTable在ABP项目里面的封装

通过上述代码你会发现,ABP在application层里面定义的方法,最终会生成某一些js对应的function,这里难点来了。我们找遍了bootstrapTable组件的api,都没有通过某一个function去获取数据的啊。这可如何是好?为这个问题,博主折腾了两天。最开始博主想,function最终还不是要换成http请求的,我们只要拿到http请求的url,然后将function转换为url不就行了么:

如何在ABP框架中使用BootstrapTable组件

我们使用bootstrapTable组件初始化的时候声明  {url:'/api/services/app/requisition/GetRequisitionListAsync'}  这样不就行了么?呵呵,经过测试,这样确实能正确取到数据。但是不够理想,因为这前面的前缀是ABP给我们生成的,是否会变化我们尚且不说,给每一个url加上这么一长串着实看着很不爽,于是进一步想,是否我们的bootstrapTable也可以使用function去初始化呢,组件没有,难道我们就不能给他扩展一个吗?我们不用url获取数据,通过调用这个function取到数据,然后将数据渲染到组件不就行了。思路有了,那么这里有两个难题:一是如何将原来url的方式变成这里的调用function的方式呢?二是参数的封装。经过查看组件的源码发现,如果是服务端分页,组件最终是进入到initServer()这个方法去获取数据,然后渲染到页面上面的,组件原始的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);   }  };

代码不难读懂,解析参数,整合参数,得到参数,发送ajax请求,在success事件里面将得到的数据渲染到界面。读懂了这段代码,我们再来封装function就容易多了。

最终我们封装的代码如下:

(function ($) {  'use strict';  //debugger;  //通过构造函数获取到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) {   //构造自定义参数   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);

代码释疑:增加两个参数 methodParams: {},abpMethod: function () { } 来获取abp的function和参数,然后获取数据的时候如果定义了abpMethod,则通过function获取数据,否则还是走原来的逻辑。

然后我们调用就简单了

//选取界面上要先数据的表格   var _$SendOrdersTable = $('#SendOrdersTable');   //获取服务层方法   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'),    },    ]   });

得到如下效果

如何在ABP框架中使用BootstrapTable组件

上述内容就是如何在ABP框架中使用BootstrapTable组件,你们学到知识或技能了吗?如果还想学到更多技能或者丰富自己的知识储备,欢迎关注亿速云行业资讯频道。

向AI问一下细节

免责声明:本站发布的内容(图片、视频和文字)以原创、转载和分享为主,文章观点不代表本网站立场,如果涉及侵权请联系站长邮箱:is@yisu.com进行举报,并提供相关证据,一经查实,将立刻删除涉嫌侵权内容。

AI