欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页  >  IT编程

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

程序员文章站 2022-05-14 19:17:26
一、关于abp abp是“asp.net boilerplate project (asp.net样板项目)”的简称,它是一个成熟的开源框架,基于ddd+reposito...

一、关于abp

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

关于abp的入门,博主不想说太多,园子里面tkb至简和阳光铭睿有很多入门级的文章,有兴趣的可以了解下,还是给出它的官网和开源地址。

abp官方网站:

abp开源项目:

ps:如果你不愿意去看它的源码,可以直接查看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组件的方法,希望对大家有所帮助