本文记录了一次使用abp Core5 ,vue 开发复杂报表的经历。
0、需求概述
业务需求是:统计一个化工厂车队形式记录数据中异常停车的报表,维度可以按照车俩、驾驶员两个维度进行统计,统计的元素有:
TLX01 堵车|TLX02 限行|TLX03 途中车辆维修|TLX04 停车休息|TLX05 雨雾天气|TLX06 装卸货或装卸货排队|TLX07 围栏停留|TLX08 吃饭|TLX09 加油|TLX10 洗车加水。
原始报表样式:
解决思路是:
1、sqlserver 分析数据源
1.1 表数据
整个报表牵扯的5张表, 分表示,停车日志、明细、要统计的停车事件类型、驾驶员信息,如下:
select * from TruckAwaitLog where LogId=‘F02D9B3E-D172-473A-8D01-FFF28A92B17C’ //停车日志
select * from TruckAwaitLogDetail where MainId=‘F02D9B3E-D172-473A-8D01-FFF28A92B17C’ //明细
select * from TruckAwaitLogType //停车事件类型
select * from [人员信息] where [人员信息].userid=‘0640’
1.2 统计数据源的sql脚本
- —异常停留类型统计表(按车俩)
SELECT truck.cCode_Name,truck.cCode, TruckAwaitLog.MainTruckId as 主驾驶,
TruckAwaitLogType.TypeId,TruckAwaitLogType.TypeName,
SUM(TruckAwaitLog.TotalDuration) as 时长, count(truck.cCode) AS 次数
FROM TruckAwaitLog
left join TruckAwaitLogType
on TruckAwaitLog.TypeId= TruckAwaitLogType.TypeId
left join [YDDATA].[dbo].[车辆] as truck
on truck.cCode = TruckAwaitLog.MainTruckId
GROUP BY truck.cCode_Name ,truck.cCode,TruckAwaitLog.MainTruckId,
TruckAwaitLogType.TypeId,TruckAwaitLogType.TypeName order by TruckAwaitLog.MainTruckId
- –异常停留类型统计表(按驾驶员)
SELECT TruckAwaitLog.TruckEscortUserId as 主驾驶,Human.cCode_Name,
TruckAwaitLogType.TypeId,TruckAwaitLogType.TypeName,
SUM(TruckAwaitLog.TotalDuration) as 时长,
count(TruckAwaitLog.LogId) AS 次数
FROM TruckAwaitLog
left join TruckAwaitLogType
on TruckAwaitLog.TypeId= TruckAwaitLogType.TypeId
inner join [YDDATA].[dbo].[V人员信息] as Human
on Human.userid = TruckAwaitLog.MainUserId
GROUP BY Human.cCode_Name, TruckAwaitLog.TruckEscortUserId,TruckAwaitLogType.TypeId,TruckAwaitLogType.TypeName
经分析后,使用多表查询,达到的报表要求, 还需处理行专列问题,由于开发框架采用ABP的DDD模式,尽量不使用原生sql,所以使用仓储模式,多模块关联,linq处理数据问题,行专列使用动态 Linq.Dynamic 构建行转列。
2、Abp 中Efcore的多模块关联查询
2.1回顾ABP 架构模型
2.2 回顾abp 的代码分层模型
2.3 回顾abp 的分层调用关系模型
2.3 ABP 模块构建
2.3.1 构建领域对象
/// <summary>
/// TruckAwaitLog,领域对象
/// </summary>
[Table("TruckAwaitLog")]
public class TruckAwaitLog : Entity<Guid>
{
/// <summary>
/// 默认构造函数(需要初始化属性的在此处理)
/// </summary>
public TruckAwaitLog()
{
}
#region Property Members
[Column("LogId")]
public override Guid Id { get; set; }
//[Required]
//[Required]
//public virtual Guid LogId { get; set; }
//[Required]
public virtual string AccId { get; set; }
//[Required]
public virtual string VouchId { get; set; }
//[Required]
[Required]
public virtual string MainUserId { get; set; }
//[Required]
public virtual string SubUserId { get; set; }
//[Required]
public virtual string TruckEscortUserId { get; set; }
ABP 重写主键ID
由于ABP设计的领域对象 Entity : IEntity有主键默认名称:id
namespace Abp.Domain.Entities
{
//
// 摘要:
// Basic implementation of IEntity interface. An entity can inherit this class of
// directly implement to IEntity interface.
//
// 类型参数:
// TPrimaryKey:
// Type of the primary key of the entity
[Serializable]
public abstract class Entity<TPrimaryKey> : IEntity<TPrimaryKey>
{
//
// 摘要:
// Unique identifier for this entity.
public virtual TPrimaryKey Id
{
get;
set;
}
//
// 摘要:
// Checks if this entity is transient (it has not an Id).
//
// 返回结果:
// True, if this entity is transient
public virtual bool IsTransient()
{
if (EqualityComparer<TPrimaryKey>.Default.Equals(Id, default(TPrimaryKey)))
{
return true;
}
if (typeof(TPrimaryKey) == typeof(int))
{
return Convert.ToInt32(Id) <= 0;
}
if (typeof(TPrimaryKey) == typeof(long))
{
return Convert.ToInt64(Id) <= 0;
}
return false;
}
public virtual bool EntityEquals(object obj)
{
if (obj == null || !(obj is Entity<TPrimaryKey>))
{
return false;
}
if (this == obj)
{
return true;
}
Entity<TPrimaryKey> entity = (Entity<TPrimaryKey>)obj;
if (IsTransient() && entity.IsTransient())
{
return false;
}
Type type = GetType();
Type type2 = entity.GetType();
if (!type.GetTypeInfo().IsAssignableFrom(type2) && !type2.GetTypeInfo().IsAssignableFrom(type))
{
return false;
}
if (this is IMayHaveTenant && entity is IMayHaveTenant && this.As<IMayHaveTenant>().TenantId != entity.As<IMayHaveTenant>().TenantId)
{
return false;
}
if (this is IMustHaveTenant && entity is IMustHaveTenant && this.As<IMustHaveTenant>().TenantId != entity.As<IMustHaveTenant>().TenantId)
{
return false;
}
return Id.Equals(entity.Id);
}
public override string ToString()
{
return $"[{GetType().Name} {Id}]";
}
}
}
所以我们表设计主键不是id的情况下要改写,规则如下。
[Column(“你那表的源主键名”)] public override leixing Id{ get; set; }>
- 重写ID [Column(“数据库指定的ID”)]>
[Column(“CarTypeID”)]
public override int Id { get; set; }
- 2.映射中指定 这里用的是AutoMapper
第一个UserID是Dto模型里面的 第二个Id是实体类中我们重写的那个Id
1 var carTypeDtoMapper = mapperConfig.CreateMap<CarType,
CarTypeDto>(); 2 carTypeDtoMapper.ForMember(dto => dto.CarTypeID, map
=> map.MapFrom(m => m.Id));
3.对于多表查询外键ID无效的情况 可以在实体中指定外键1 [ForeignKey(“CarBrand”)] 2 public int? BrandID { get; set; }
对于ForeignKeyVS给我们做了良好的解释>
1 //如果将 ForeigKey 特性添加到外键属性,则应指定关联的导航属性的名称。如果将 ForeigKey
特性添加到导航属性,则应指定关联的外键的名称。如果导航属性具有多个外键,则使用逗号分隔的外键名称列表。
public ForeignKeyAttribute(string name);
4.获取数据 将数据返回 这里用的是DTO模型传输数据>
1 var list = _carTypeRepository.GetAllList();
2 将POCO对象转为DTO对象
3 return list.MapTo<List>();
2.3.2 构建基础设施对象
public partial class SecondDbContext : AbpZeroDbContext<Tenant, Role, User, SecondDbContext>, IAbpPersistedGrantDbContext
{
/// <summary>
/// TruckAwaitLog,数据表对象
/// </summary>
public virtual DbSet<TruckAwaitLog> TruckAwaitLogs { get; set; }
/// <summary>
/// TruckAwaitLogDetail,数据表对象
/// </summary>
public virtual DbSet<TruckAwaitLogDetail> TruckAwaitLogDetails { get; set; }
/// <summary>
/// TruckAwaitLogType,数据表对象
/// </summary>
public virtual DbSet<TruckAwaitLogType> TruckAwaitLogTypes { get; set; }
/// <summary>
/// TruckStatus,数据表对象
/// </summary>
public virtual DbSet<TruckStatus> TruckStatuss { get; set; }
/// <summary>
/// 车辆,数据表对象
/// </summary>
public virtual DbSet<车辆> 车辆s { get; set; }
/// <summary>
/// V人员信息,数据表对象
/// </summary>
public virtual DbSet<V人员信息> V人员信息s { get; set; }
//IdentityServer接口实现
public virtual DbSet<PersistedGrantEntity> PersistedGrants { get; set; }
public SecondDbContext(DbContextOptions<SecondDbContext> options) : base(options)
{
}
}
}
2.3.2 构建应用层对象
/// <summary>
/// TruckAwaitLog,应用层服务接口实现
/// </summary>
[DisableAuditing]
[AbpAuthorize]
public class TruckAwaitLogAppService : MyAsyncServiceBase<TruckAwaitLog, TruckAwaitLogDto, Guid, TruckAwaitLogPagedDto, CreateTruckAwaitLogDto, TruckAwaitLogDto>, ITruckAwaitLogAppService
{
private readonly IRepository<TruckAwaitLog, Guid> _truckAwaitLogRepository;
private readonly IRepository<TruckAwaitLogDetail, string> _truckAwaitLogDetailRepository;
private readonly IRepository<TruckAwaitLogType, string> _truckAwaitLogTypeRepository;
private readonly IRepository<车辆, string> _truckRepository;
private readonly IRepository<V人员信息, string> _renYuanRepository;
private readonly IRepository<User, long> _userRepository;//用户信息仓储对象
public TruckAwaitLogAppService(
IRepository<TruckAwaitLog, Guid> truckAwaitLogRepository,
IRepository<TruckAwaitLogDetail, string> truckAwaitLogDetailRepository,
IRepository<TruckAwaitLogType, string> truckAwaitLogTypeRepository,
IRepository<车辆, string> truckRepository,
IRepository<V人员信息, string> renYuanRepository,
IRepository<User, long> userRepository) : base(truckAwaitLogRepository)
{
_truckAwaitLogRepository = truckAwaitLogRepository;
_truckAwaitLogDetailRepository = truckAwaitLogDetailRepository;
_truckAwaitLogTypeRepository = truckAwaitLogTypeRepository;
_truckRepository = truckRepository;
_renYuanRepository = renYuanRepository;
_userRepository = userRepository;
}
2.4 多模块关联查询、分组统计、列转行
-在 Application.Service 模块构建联合查询
//Linq 构建关联查询 -异常停留类型统计表(按车俩)
//构建关联查询
var query = from truckAwaitLog in _truckAwaitLogRepository.GetAll()
join truckAwaitLogType in _truckAwaitLogTypeRepository.GetAll() on truckAwaitLog.TypeId equals truckAwaitLogType.Id
join truck in _truckRepository.GetAll() on truckAwaitLog.MainTruckId equals truck.Id
where truckAwaitLog.AddDate >= input.AddDateStart && truckAwaitLog.AddDate <= input.AddDateEnd
select new TruckAwaitLogQueryDto
{
NameID = truck.Id,
Name = truck.CCode_Name,
// StopEventAreaTypeID = truckAwaitLogType.TypeId,
StopEventArea = truckAwaitLogType.TypeName,
TotalDuration = truckAwaitLog.TotalDuration
};
//构建关联查询结构上实现分组统计
var queryColumn = (from m in query
group m by
//new { m.NameID, m.Name, m.StopEventArea, m.StopEventAreaTypeID }
new { m.NameID, m.Name, m.StopEventArea }
into g
select new TruckAwaitLogQueryDto
{
NameID = g.Key.NameID,
Name = g.Key.Name + g.Key.NameID,
// StopEventAreaTypeID = g.Key.StopEventAreaTypeID,
StopEventArea = g.Key.StopEventArea,
TotalDuration = g.Sum(p => p.TotalDuration),
Numbers = g.Count()
}).ToList();
```csharp
//Linq 构建关联查询 -异常停留类型统计表(按驾驶员)
//构建关联查询
var query = from truckAwaitLog in _truckAwaitLogRepository.GetAll()
join truckAwaitLogType in _truckAwaitLogTypeRepository.GetAll() on truckAwaitLog.TypeId equals truckAwaitLogType.Id
join renyuan in _renYuanRepository.GetAll() on truckAwaitLog.MainUserId equals renyuan.Id
where truckAwaitLog.AddDate >= input.AddDateStart && truckAwaitLog.AddDate <= input.AddDateEnd
select new TruckAwaitLogQueryDto
{
NameID = renyuan.Id,
Name = renyuan.CCode_Name,
// StopEventAreaTypeID = truckAwaitLogType.TypeId,
StopEventArea = truckAwaitLogType.TypeName,
TotalDuration = truckAwaitLog.TotalDuration
};
//构建关联查询结构上实现分组统计
var queryColumn = (from m in query
group m by
new { m.NameID, m.Name, m.StopEventArea }
into g
select new TruckAwaitLogQueryDto
{
NameID = g.Key.NameID,
Name = g.Key.Name + g.Key.NameID,
// StopEventAreaTypeID = g.Key.StopEventAreaTypeID,
StopEventArea = g.Key.StopEventArea,
TotalDuration = g.Sum(p => p.TotalDuration),
Numbers = g.Count()
}).ToList();
-在 Application.Service 封装完整代码供客户端调用
/// <summary>
/// 自定义条件处理,暴漏API
/// </summary>
/// <param name="input">查询条件Dto</param>
/// <returns></returns>
[DisableAuditing]
public async Task<List<dynamic>> CreateReportDataSourceWithUserQueryAsync(TruckAwaitLogPagedDto input)
{
//报表统计的维度 : 前台传过来分组维度和动态列字段
//DEMO: 分组维度:{DimensionList:['Name'] 车辆|人员
//动态列字段:DynamicColumn:'StopEventArea'}
List<string> DimensionList = new List<string>() { "Name" };
string DynamicColumn = "StopEventArea";
List<string> AllDynamicColumn = null;
if (input.QueryType == "按车辆")//按车辆
{
//构建关联查询
var query = from truckAwaitLog in _truckAwaitLogRepository.GetAll()
join truckAwaitLogType in _truckAwaitLogTypeRepository.GetAll() on truckAwaitLog.TypeId equals truckAwaitLogType.Id
join truck in _truckRepository.GetAll() on truckAwaitLog.MainTruckId equals truck.Id
where truckAwaitLog.AddDate >= input.AddDateStart && truckAwaitLog.AddDate <= input.AddDateEnd
select new TruckAwaitLogQueryDto
{
NameID = truck.Id,
Name = truck.CCode_Name,
// StopEventAreaTypeID = truckAwaitLogType.TypeId,
StopEventArea = truckAwaitLogType.TypeName,
TotalDuration = truckAwaitLog.TotalDuration
};
var queryColumn = (from m in query
group m by
//new { m.NameID, m.Name, m.StopEventArea, m.StopEventAreaTypeID }
new { m.NameID, m.Name, m.StopEventArea }
into g
select new TruckAwaitLogQueryDto
{
NameID = g.Key.NameID,
Name = g.Key.Name + g.Key.NameID,
// StopEventAreaTypeID = g.Key.StopEventAreaTypeID,
StopEventArea = g.Key.StopEventArea,
TotalDuration = g.Sum(p => p.TotalDuration),
Numbers = g.Count()
}).ToList();
return await Task.FromResult(MyProject.Application.HY.OLAP.Report.Helper.ReportUtility.
DynamicRow2ColeByLinq(queryColumn, DimensionList, DynamicColumn, out AllDynamicColumn));
}
else if (input.QueryType == "按驾驶员")//按人员
{
//构建关联查询
var query = from truckAwaitLog in _truckAwaitLogRepository.GetAll()
join truckAwaitLogType in _truckAwaitLogTypeRepository.GetAll() on truckAwaitLog.TypeId equals truckAwaitLogType.Id
join renyuan in _renYuanRepository.GetAll() on truckAwaitLog.MainUserId equals renyuan.Id
where truckAwaitLog.AddDate >= input.AddDateStart && truckAwaitLog.AddDate <= input.AddDateEnd
select new TruckAwaitLogQueryDto
{
NameID = renyuan.Id,
Name = renyuan.CCode_Name,
// StopEventAreaTypeID = truckAwaitLogType.TypeId,
StopEventArea = truckAwaitLogType.TypeName,
TotalDuration = truckAwaitLog.TotalDuration
};
var queryColumn = (from m in query
group m by
new { m.NameID, m.Name, m.StopEventArea }
into g
select new TruckAwaitLogQueryDto
{
NameID = g.Key.NameID,
Name = g.Key.Name + g.Key.NameID,
// StopEventAreaTypeID = g.Key.StopEventAreaTypeID,
StopEventArea = g.Key.StopEventArea,
TotalDuration = g.Sum(p => p.TotalDuration),
Numbers = g.Count()
}).ToList();
//动态Linq方式实现行转列
return await Task.FromResult(MyProject.Application.HY.OLAP.Report.Helper.ReportUtility.
DynamicRow2ColeByLinq(queryColumn, DimensionList, DynamicColumn, out AllDynamicColumn));
// return result List<dynamic> result = ;
}
return null;
}
2.5.使用Linq.Dynamic 构建行转列为报表提供数据源
需要nuget 引入 Linq.Dynamic。
传入原始数据源
/// <summary>
/// 报表工具类
/// 须在nuget引入System.Linq.Dynamic
/// </summary>
public class ReportUtility
{ // <summary>
/// 动态Linq方式实现行转列
/// </summary>
/// <param name="listSource">原始数据集合</param>
/// <param name="DimensionList">维度列(分组列)</param>
/// <param name="DynamicColumn">动态列(要生成的列)</param>
/// <returns>行转列后数据</returns>
//private List<TruckAwaitLogQueryDto> GetTruckAwaitLogQueryDtoList(List<TruckAwaitLogType> listTypes, List<EntityTruckAwaitLogByVehicleUser> entityTruckAwaitLogBies)
public static List<dynamic> DynamicRow2ColeByLinq<T>(List<T> listSource, List<string> DimensionList, string DynamicColumn, out List<string> AllDynamicColumn) where T : class
{
//获取所有动态列
var columnGroup = listSource.GroupBy(DynamicColumn, "new(it as Vm)") as IEnumerable<IGrouping<dynamic, dynamic>>;
List<string> AllColumnList = new List<string>();
foreach (var item in columnGroup)
{
if (!string.IsNullOrEmpty(item.Key))
{
AllColumnList.Add(item.Key);
}
}
AllDynamicColumn = AllColumnList;
var dictFunc = new Dictionary<string, Func<T, bool>>();
foreach (var column in AllColumnList)
{
var func = DynamicExpression.ParseLambda<T, bool>(string.Format("{0}==\"{1}\"", DynamicColumn, column)).Compile();
dictFunc[column] = func;
}
//获取实体所有属性
Dictionary<string, PropertyInfo> PropertyInfoDict = new Dictionary<string, PropertyInfo>();
Type type = typeof(T);
var propertyInfos = type.GetProperties(BindingFlags.Instance | BindingFlags.Public);
//数值列
List<string> AllNumberField = new List<string>();
foreach (var item in propertyInfos)
{
PropertyInfoDict[item.Name] = item;
if (item.PropertyType == typeof(int) || item.PropertyType == typeof(double) || item.PropertyType == typeof(float))
{
AllNumberField.Add(item.Name);
}
}
//分组
var dataGroup = listSource.GroupBy(string.Format("new ({0})", string.Join(",", DimensionList)), "new(it as Vm)") as IEnumerable<IGrouping<dynamic, dynamic>>;
List<dynamic> listResult = new List<dynamic>();
IDictionary<string, object> itemObj = null;
T vm2 = default(T);
foreach (var group in dataGroup)
{
itemObj = new ExpandoObject();
var listVm = group.Select(e => e.Vm as T).ToList();
//维度列赋值
vm2 = listVm.FirstOrDefault();
foreach (var key in DimensionList)
{
itemObj[key] = PropertyInfoDict[key].GetValue(vm2);
}
foreach (var column in AllColumnList)
{
vm2 = listVm.FirstOrDefault(dictFunc[column]);
if (vm2 != null)
{
foreach (string name in AllNumberField)
{
itemObj[name + column] = PropertyInfoDict[name].GetValue(vm2);
}
}
}
listResult.Add(itemObj);
}
return listResult;
}
}
该方法处理后的数据如下。
3、 VUE TABLE 多级表头实现
数据结构比较复杂的时候,可使用多级表头来展现数据的层次关系。
3.1 导入业务API对象
import truckAwaitLog from '@/api/HYReport/truckawaitlog' // 停车数据业务操作对象
import truckAwaitLogType from '@/api/HYReport/truckawaitlogtype' // 停车事件业务操作对象
```css
class Api extends BaseApi {
GetReportDataSource(data) {
return request({
url: this.baseurl + 'CreateReportDataSourceWithUserQueryAsync',
method: 'get',
params: data
})
}
class Api extends BaseApi {
GetStopEventAreas() {
return request({
url: this.baseurl + 'GetTruckAwaitLogType',
method: 'get'
})
}
3.2 vue 查询面板
查询面板值得是查询功能区布局,包含查询条件的组合,功能按钮等, 比如多条件下拉组合、时间选择、类型选择等。
<pane min-size="10" max-size="20">
<el-card class="box-card" style="background-color:#eef1f6">
<div slot="header" class="clearfix" style="text-align:center">
<span>
<i class="el-icon-s-grid" />
异常停留类型查询面板
<i class="el-icon-s-grid" />
</span>
</div>
</el-card>
<el-card class="box-card" style="background-color:#eef1f6">
<!--查询条件区域 -->
<el-form ref="searchForm" :model="searchForm" label-width="100px" :inline="true">
<template>
<el-form-item label="统计时间">
<el-date-picker v-model="searchForm.AddDate" type="daterange" align="right" unlink-panels range-separator="至" start-placeholder="开始日期" end-placeholder="结束日期" :picker-options="pickerOptions" />
</el-form-item>
</template>
<template>
<div>
<el-radio-group v-model="radiorules">
<el-radio-button label="按驾驶员" />
<el-radio-button label="按车辆" />
</el-radio-group>
</div>
</template>
</el-form>
</el-card>
<el-card class="box-card" style="background-color:#eef1f6">
<template>
<!--功能操作按钮 -->
<el-row style="float:right;padding-bottom:10px">
<el-button-group>
<el-button icon="el-icon-search" type="primary" size="mini" round @click="search()">查询</el-button>
<el-button icon="el-icon-refresh-left" type="warning" size="mini" round plain @click="resetSeachForm('searchForm')">重置</el-button>
</el-button-group>
<el-button-group>
<el-button icon="el-icon-download" :loading="downloadLoading" type="primary" size="mini" round @click="handleDownload()">导出</el-button>
</el-button-group>
</el-row>
</template>
</el-card>
</pane>
3.2 vue 查询数据展示区
实际的数据查询结果。
组合表头,列合并分组
嵌套实现组合
动态列表头的生成
StopEventArea 是通过import truckAwaitLogType from ‘@/api/HYReport/truckawaitlogtype’ // 停车事件业务操作对象获取的事件类型。
动态列表头的数据源关联获取
<el-table-column v-for="(item,i) in StopEventArea" :key="i" :prop="item.typeId" :label="item.typeName" align="center">
<el-table-column :key="item.typeId" :prop="`Numbers`+item.typeName" label="次数" :formatter="NumbersFun" />
<el-table-column :key="item.typeId" :prop="`TotalDuration`+item.typeName" label="时长(分)" :formatter="TotalDurationFun" />
</el-table-column>
:label=“item.typeName”
:prop=“
Numbers
+item.typeName”
完整代码
这里注意返回数据的结构, vue遍历的时候动态变量赋值问题。
<pane style="background-color:#fff" title="异常停留类型统计表">
<!--表格列表信息 -->
<el-table v-loading="listLoading" :data="listSources" border fit stripe highlight-current-row :header-cell-style="{background:'#eef1f6',color:'#606266'}" @selection-change="selectionChange" @sort-change="sortChange">
<el-table-column align="center" sortable="custom" prop="Name" :label="this.radioVal" width="155">
<template slot-scope="scope">
<router-link tag="a" :to="{path:'/orderDetail',query:{id:scope.row.NameID}}">{{ scope.row.Name }}
</router-link>
</template>
</el-table-column>
<el-table-column v-for="(item,i) in StopEventArea" :key="i" :prop="item.typeId" :label="item.typeName" align="center">
<el-table-column :key="item.typeId" :prop="`Numbers`+item.typeName" label="次数" :formatter="NumbersFun" />
<el-table-column :key="item.typeId" :prop="`TotalDuration`+item.typeName" label="时长(分)" :formatter="TotalDurationFun" />
</el-table-column>
</el-table>
<!--分页部分 -->
<div class="block" style="height:70px;">
<el-pagination :current-page="pageinfo.pageindex" :page-size="pageinfo.pagesize" :total="pageinfo.total" :page-sizes="[10,20,30,40]" layout="total, sizes, prev, pager, next" @size-change="sizeChange" @current-change="currentChange" />
</div>
</pane>