前言:关于ABP框架,博主关注差很少有两年了吧,一直迟迟没有尝试。一方面博主以为像这种复杂的开发框架确定有它的过人之处,系统的稳定性和健壮性比通常的开源框架确定强不少,但是另外一方面往往想到它繁琐的封装和复杂的开发流程就望而却步,就这样迟迟没有行动。最近在项目里面用到了ABP框架,没办法,只有硬着头皮上了。通过这一段时间的熟悉,算是对这个框架有了一个大概的了解。今天来分享下如何在ABP框架的模式里面使用bootstrapTable组件。html
本文原创地址:http://www.cnblogs.com/landeanfen/p/7261651.html前端
ABP是“ASP.NET Boilerplate Project (ASP.NET样板项目)”的简称,它是一个成熟的开源框架,基于DDD+Repository模式,自带Zero权限和认证模块,避免了从零开始搭建框架的烦恼。关于ABP的框架优点就此打住,由于这样说下去要说三天三夜,脱离文本主题。git
关于ABP的入门,博主不想说太多,园子里面tkb至简和阳光铭睿有不少入门级的文章,有兴趣的能够了解下,仍是给出它的官网和开源地址。github
ABP官方网站:http://www.aspnetboilerplate.comajax
ABP开源项目:https://github.com/aspnetboilerplatejson
PS:若是你不肯意去看它的源码,能够直接查看ABP官网上面的演示地址:https://aspnetzero.com/?ref=abptmplpagebootstrap
点击CREATE MY DEMO按钮,系统会自动为你生成演示地址api
进入对应的Demo URLapp
使用演示的用户名和密码登录进去框架
能够看到Zero模块的实现效果。
若是你下载ABP的源码,而且选择的是混合开发模式(ABP提供了两种开发模式,一种是基于MVVM的Angular.js的模式;另外一种就是MVC+jQuery的混合开发模式),以下图:
当你Down下来源码以后你就会发现,ABP的源码里面的UI部分的表格都是使用jTable去实现的。为何会用jTable?缘由很简单,jTable是ABP的做者kalkan写的一款开源插件,本身写的确定用本身的东西喽。下面jTable的效果来一发。
来一个jtable的父子表:
若是是不带父子表的简单表格,其实jTable的效果其实还行,但是加上一些复杂的功能以后,那一片片蓝色的区域不忍直视,而且jTable的api还有待完善,不少须要的功能都须要本身去实现,因而就接到了将全部的表格组件换成BootstrapTable的需求,才有了今天的主题:在ABP中封装BootstrapTable。
接到需求,博主各类百度、各类谷歌,都找不到Bootstrap Table组件在ABP中的封装,有的只是在ABP的项目里面简单的用传统的方式去初始化组件,这并非博主想要的。说到这里不得不说一下,若是你使用ABP开发的过程当中遇到一些难题,你会发现很难从百度里面搜索到相关答案,谷歌里面有时能找到,但大部分都是英文社区,因此若是你英文较弱,在查找资料上面会很吃亏,有时一个简单的配置问题须要折腾好久。
首先来看看jTable在通常的ABP项目里面是如何初始化的。好比咱们在Application里面有一个以下的接口和实现
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>' } } } }); }); })();
获得以下效果:
代码释疑:
(1) var _service = abp.services.app.requisition; 这一句声明当前页面须要使用哪一个服务。
(2) _service.getRequisitionListAsync 这一句对应的是服务调用的方法,你会发如今后台方法名是GetRequisitionListAsync(),而在js里面却变成了getRequisitionListAsync(),咱们暂且称之为“潜规则”。
经过上述代码你会发现,ABP在application层里面定义的方法,最终会生成某一些js对应的function,这里难点来了。咱们找遍了bootstrapTable组件的api,都没有经过某一个function去获取数据的啊。这可如何是好?为这个问题,博主折腾了两天。最开始博主想,function最终还不是要换成http请求的,咱们只要拿到http请求的url,而后将function转换为url不就好了么:
咱们使用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() ? 0 : 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'), }, ] });
获得以下效果
经过以上一个简单的封装,顺利将bootstrapTable融入到了ABP的操做方式里面。是否是很easy!在使用ABP的过程当中,博主还作了其余一些封装,之后有机会再来介绍,关于ABP的技术交流欢迎联系博主。这一篇就到这里,欢迎交流。若是你以为本文可以帮助你,能够右边随意 打赏 博主,打赏后能够得到博主永久免费的技术支持。
本文原创出处:http://www.cnblogs.com/landeanfen/
欢迎各位转载,可是未经做者本人赞成,转载文章以后必须在文章页面明显位置给出做者和原文链接,不然保留追究法律责任的权利