打开APP
userphoto
未登录

开通VIP,畅享免费电子书等14项超值服

开通VIP
JS组件系列——在ABP中封装BootstrapTable

 

正文

 前言:关于ABP框架,博主关注差不多有两年了吧,一直迟迟没有尝试。一方面博主觉得像这种复杂的开发框架肯定有它的过人之处,系统的稳定性和健壮性比一般的开源框架肯定强很多,可是另一方面每每想到它繁琐的封装和复杂的开发流程就望而却步,就这样迟迟没有行动。最近在项目里面用到了ABP框架,没办法,只有硬着头皮上了。经过这一段时间的熟悉,算是对这个框架有了一个大概的了解。今天来分享下如何在ABP框架的模式里面使用bootstrapTable组件。

本文原创地址:http://www.cnblogs.com/landeanfen/p/7261651.html

一、关于ABP

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

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

ABP官方网站http://www.aspnetboilerplate.com

ABP开源项目https://github.com/aspnetboilerplate

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

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

进入对应的Demo URL

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

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

二、jTable在ABP中的运用

如果你下载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中的封装

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

1、jTable在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(),我们暂且称之为“潜规则”。

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

通过上述代码你会发现,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()方法如下:

组件原始initServer()方法

代码不难读懂,解析参数,整合参数,得到参数,发送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/

本站仅提供存储服务,所有内容均由用户发布,如发现有害或侵权内容,请点击举报
打开APP,阅读全文并永久保存 查看更多类似文章
猜你喜欢
类似文章
【热】打开小程序,算一算2024你的财运
iframe标签很适合ThingJS框架,3D开发更轻松
bootstrap 复选框
一个音乐播放器的js源码 及页面
PHP中使用imagick实现把PDF转成图片
javascript+xml实现二级下拉菜单,不会被任何标签或元素遮住
Tree 二次从新加载调用方法
更多类似文章 >>
生活服务
热点新闻
分享 收藏 导长图 关注 下载文章
绑定账号成功
后续可登录账号畅享VIP特权!
如果VIP功能使用有故障,
可点击这里联系客服!

联系客服