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

一、关于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()方法如下:

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组件的方法,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对我们网站的支持!

(0)

相关推荐

  • JS组件系列之Bootstrap table表格组件神器【终结篇】

    bootstrap table系列: JS表格组件神器bootstrap table详解(基础版) JS组件系列之Bootstrap table表格组件神器[终结篇] JS组件系列之Bootstrap table表格组件神器[二.父子表和行列调序] Bootstrap Table是轻量级的和功能丰富的以表格的形式显示的数据,支持单选,复选框,排序,分页,显示/隐藏列,固定标题滚动表,响应式设计,Ajax加载JSON数据,点击排序的列,卡片视图等.那么本文给大家介绍JS组件系列之Bootstrap

  • JS组件Bootstrap Table表格多行拖拽效果实现代码

    前言:前天刚写了篇JS组件Bootstrap Table表格行拖拽效果,今天接到新的需要,需要在之前表格行拖拽的基础上能够同时拖拽选中的多行.用了半天时间研究了下,效果是出来了,但是感觉不尽如人意.先把它分享出来,以后想到更好的办法再优化吧. 一.效果展示 1.拖动前 2.拖动中 3.拖动后 4.撤销回到拖动前状态 二.需求分析 通过上篇我们知道,如果要实现拖拽,必须要有一个可以拖拽的标签,或者叫容器,比如上篇里面的tr就是一个拖拽的容器,那么如果要实现选择行的拖拽,那么博主的第一反应是将选中的

  • 解决JS组件bootstrap table分页实现过程中遇到的问题

    本文为大家分享了bootstrap-table 分页的问题,供大家参考,具体内容如下 问题1 :服务器端取不到form值,querystring没有问题,但是request.form取不到值 解决:这是ajax的问题,原代码使用原生的ajax.   1可以用读流文件解决.2 如果想用request.form 方式,设置  contentType: "application/x-www-form-urlencoded", 如 $('#tableList').bootstrapTable(

  • JS表格组件神器bootstrap table详解(强化版)

    一.Bootstrap Table的引入 关于Bootstrap Table的引入,一般来说还是两种方法: 1.直接下载源码,添加到项目里面来. 由于Bootstrap Table是Bootstrap的一个组件,所以它是依赖Bootstrap的,我们首先需要添加Bootstrap的引用. 2.使用我们神奇的Nuget 打开Nuget,搜索这两个包 Bootstrap已经是最新的3.3.5了,我们直接安装即可. 而Bootstrap Table的版本竟然是0.4,这也太坑爹了.所以博主建议Boot

  • JS组件系列之Bootstrap table表格组件神器【二、父子表和行列调序】

    Bootstrap Table是轻量级的和功能丰富的以表格的形式显示的数据,支持单选,复选框,排序,分页,显示/隐藏列,固定标题滚动表,响应式设计,Ajax加载JSON数据,点击排序的列,卡片视图等.今天就结合Bootstrap table的父子表和行列调序的用法再来介绍下它稍微高级点的用法. bootstrap table系列: JS表格组件神器bootstrap table详解(基础版) JS组件系列之Bootstrap table表格组件神器[终结篇] JS组件系列之Bootstrap t

  • JS组件Bootstrap Table表格行拖拽效果实现代码

    一.业务需求及实现效果 项目涉及到订单模块,那天突然接到一个需求,说是两种不同状态的订单之间要实现插单的效果,页面上呈现方式是:左右两个Table,左边Table里面是状态为1的订单,右边Table里面是状态为2订单,左边Table里面的行数据拖动到右边Table里面指定行的位置,拖动完成后,左边表格减少一行,右边表格增加一行.除此之外,还需要撤销操作(相当于Ctrl + Z操作),能够返回到上一步的状态.可能描述会让大家模拟两可,反正已经实现了,先来看看效果图吧. 1.先看看拖动之前的效果 2

  • JS组件Bootstrap Table使用方法详解

    最近客户提出需求,想将原有的管理系统,做下优化,通过手机也能很好展现,想到2个方案: a方案:保留原有的页面,新设计一套适合手机的页面,当手机访问时,进入m.zhy.com(手机页面),pc设备访问时,进入www.zhy.com(pc页面) b方案:采用bootstrap框架,替换原有页面,自动适应手机.平板.PC 设备 采用a方案,需要设计一套界面,并且要得重新写适合页面的接口,考虑到时间及成本问题,故项目采用了b方案 一.效果展示 二.BootStrap table简单介绍 bootStra

  • JS表格组件神器bootstrap table详解(基础版)

    一.Bootstrap Table的引入 关于Bootstrap Table的引入,一般来说还是两种方法: 1.直接下载源码,添加到项目里面来. 由于Bootstrap Table是Bootstrap的一个组件,所以它是依赖Bootstrap的,我们首先需要添加Bootstrap的引用. 2.使用我们神奇的Nuget 打开Nuget,搜索这两个包 Bootstrap已经是最新的3.3.5了,我们直接安装即可. 而Bootstrap Table的版本竟然是0.4,这也太坑爹了.所以博主建议Boot

  • JS组件Bootstrap Table布局详解

    Bootstrap 提供了一个清晰的创建表格的布局.下表列出了 Bootstrap 支持的一些表格元素: 表格类 下表样式可用于表格中: <tr>, <th> 和 <td> 类 下表的类可用于表格的行或者单元格: 基本的表格 如果您想要一个只带有内边距(padding)和水平分割的基本表,请添加 class .table,如下面实例所示: <div class="row"> <table class="table"

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

    一.关于ABP ABP是"ASP.NET Boilerplate Project (ASP.NET样板项目)"的简称,它是一个成熟的开源框架,基于DDD+Repository模式,自带Zero权限和认证模块,避免了从零开始搭建框架的烦恼.关于ABP的框架优势就此打住,因为这样说下去要说三天三夜,脱离文本主题. 关于ABP的入门,博主不想说太多,园子里面tkb至简和阳光铭睿有很多入门级的文章,有兴趣的可以了解下,还是给出它的官网和开源地址. ABP官方网站:http://www.aspn

  • 详解ABP框架中Session功能的使用方法

    如果一个应用程序需要登录,则它必须知道当前用户执行了什么操作.因此ASP.NET在展示层提供了一套自己的SESSION会话对象,而ABP则提供了一个可以在任何地方 获取当前用户和租户的IAbpSession接口. 关于IAbpSession 需要获取会话信息则必须实现IAbpSession接口.虽然你可以用自己的方式去实现它(IAbpSession),但是它在module-zero项目中已经有了完整的实现. 注入Session IAbpSession通常是以属性注入的方式存在于需要它的类中,不需

  • ABP框架中的事件总线功能介绍

    目录 事件总线 关于事件总线 为什么需要这个东西 事件总线创建过程 订阅事件 事件 发布事件 全局异常加入事件总线功能 创建事件 订阅事件 发布事件 测试 记录事件 事件总线 关于事件总线 ABP 中,为了方便进程间通讯,给开发者提供了一个叫 事件总线 的功能,事件总线分为 本地事件总线.分布式事件总线,本篇文章讲的是 本地事件总线,系列教程中暂时不考虑讲解 分布式事件总线. 事件总线 需要使用 Volo.Abp.EventBus 库,ABP 包中自带,不需要额外引入. 事件总线是通过 订阅-发

  • Python:Scrapy框架中Item Pipeline组件使用详解

    Item Pipeline简介 Item管道的主要责任是负责处理有蜘蛛从网页中抽取的Item,他的主要任务是清晰.验证和存储数据. 当页面被蜘蛛解析后,将被发送到Item管道,并经过几个特定的次序处理数据. 每个Item管道的组件都是有一个简单的方法组成的Python类. 他们获取了Item并执行他们的方法,同时他们还需要确定的是是否需要在Item管道中继续执行下一步或是直接丢弃掉不处理. Item管道通常执行的过程有 清理HTML数据 验证解析到的数据(检查Item是否包含必要的字段) 检查是

  • Vue3中10多种组件通讯方法小结

    目录 Props emits expose / ref Non-Props 单个根元素的情况 多个元素的情况 v-model 单值的情况 多个 v-model 绑定 v-model 修饰符 插槽 slot 默认插槽 具名插槽 作用域插槽 provide / inject 总线 bus getCurrentInstance Vuex State Getter Mutation Action Module Pinia 安装 注册 mitt.js 安装 使用 本文讲解 Vue 3.2 组件多种通讯方式

  • Laravel5.4框架中视图共享数据的方法详解

    本文实例讲述了Laravel5.4框架中视图共享数据的方法.分享给大家供大家参考,具体如下: 每个人都会遇到这种情况:某些数据还在每个页面进行使用,比如用户信息,或者菜单数据,最基本的做法是在每个视图空控制器中传入这些数据,但显然并不是我们想要的结果.另一种方法就是使用视图数据共享,视图数据共享的基本使用很简单,可查看视图文档了解详情,这里我们演示两个使用示例:在视图间共享数据和视图Composer 在视图中共享数据 除了在单个视图中传递指定数据之外,有时候需要在所有视图中传入同一数据,即我们需

  • 在laravel框架中使用model层的方法

    创建model:php artisan make:model privilegeModel(名字随便写,可以不加Model) 控制器层加载model <?php namespace App\Http\Controllers\Admin; //注意命名空间 use App\Http\Controllers\Controller; use app\privilegeModel; function index(){ $model=new \App\PrivilegeModel(); //实例化mode

  • laravel框架中视图的基本使用方法分析

    本文实例讲述了laravel框架中视图的基本使用方法.分享给大家供大家参考,具体如下: laravel中的视图默认保存在 resources\views 目录下.在控制器中,我们通常使用 view() 方法返回一个视图文件. <?php namespace App\Http\Controllers; use Illuminate\Http\Request; class TestController extends Controller { public function test(Request

  • YII2框架中验证码的简单使用方法示例

    本文实例讲述了YII2框架中验证码的简单使用方法.分享给大家供大家参考,具体如下: 验证码的使用是比较频繁的.YII2中已经帮我们做好了封装. 首先我们在控制器里创建一个actions方法,用于使用yii\captcha\CaptchaAction <?php namespace app\controllers; use YII; use yii\web\Controller; class IndexController extends Controller { public function

  • React中使用antd组件的方法

    目录 antd 使用antd antd antd 是基于 Ant Design 设计体系的 React UI 组件库,主要用于研发企业级中后台产品. 国内镜像为:https://ant-design.gitee.io/docs/react/introduce-cn速度很快 进入网页点击组件就可以看到网页需要的各种配件,如按钮.导航栏等等,并且配有各种使用方法的API,目前已经更新到4.22版本,原本是只支持react不过后来也支持vue了.3.几版本的文档说明会更加详细antd还可以更改主题颜色

随机推荐