记React+.NetCore API实现动态列导出
1.效果演示

2.用到的第三方类库
前端:React,Dva,Antd
后端:ASP.NET CORE,System.Linq.Dynamic.Core,EPPlus.Core
3.基本思路
第一:EF(LINQ)解决动态列的问题,最开始的思路是直接写SQL,后来想想太Low,就放弃了。百度了下,还真有解决方案,System.Linq.Dynamic.Core
第二:.NetCore解决生成Excel文件(流),使用了EPPlus.Core。
第三:解决文件下载的问题,最开始的思路是,直接传流到前端,前端读response.blob(),然后生成文件,结果......失败,因为所有请求要先过Node层,流经过一次转发后,死活没办法变成excel文件。前端能力不强,所以放弃这个思路了。
按照后端的解决方式,就是直接把文件生成到服务端,然后返回路径给前端,前端跳转一次,就可以实现下载,尝试,成功!
4.核心代码
代码只是初版代码,实现了基础需求。仅做参考
后端部分
请求模型:
public class ExportRequestModel<T>
{ public List<ExportRowModel> RowRequest { get; set; } /// <summary>
/// 导出人ID
/// </summary>
public string ExportUserID { get; set; } /// <summary>
/// 导出模块名称
/// </summary>
public string ModuleName { get; set; } public T Where { get; set; }
} public class ExportRowModel
{
public string RowName { get; set; } public string RowKey { get; set; } public string TableName { get; set; }
}
ExcelHelper.cs:
public static string Export(ExportRequestModel<TWhere> THead, List<TBody> TBody, string LocalPath)
{
try
{
string sFileName = $"{THead.ExportUserID}-{Guid.NewGuid()}.xlsx";
var finallypath = LocalPath + "\\excel\\" + THead.ModuleName;
DirectoryInfo di = new DirectoryInfo(finallypath);
if (!di.Exists) { di.Create(); }
FileInfo file = new FileInfo(Path.Combine(finallypath, sFileName)); var pack = GetExcelPackage(THead.RowRequest, TBody);
pack.SaveAs(file); return ("\\excel\\" + THead.ModuleName + "\\" + sFileName).Replace("\\", "/");
}
catch (Exception)
{
throw;
} } private static ExcelPackage GetExcelPackage(List<ExportRowModel> THead, List<TBody> TBody)
{
ExcelPackage package = new ExcelPackage(); var worksheet = package.Workbook.Worksheets.Add("sheet1");
//循环生成表头
for (int i = ; i <= THead.Count; i++)
{
worksheet.Cells[, i].Value = THead[i - ].RowName;
} //循环值
for (int i = ; i <= TBody.Count; i++)
{
var body = TBody[i - ];
var bodyPro = body.GetType().GetProperties();
for (int j = ; j <= bodyPro.Length; j++)
{
if (bodyPro[j - ].Name == "Item")
{
break;
}
worksheet.Cells[i + , j].Value = bodyPro[j - ].GetValue(body, null);
}
}
return package; }
}
示例代码
ApiResult<string> apiResult = new ApiResult<string>();
List<string> selectKey = new List<string>();
foreach (var item in request.RowRequest)
{
var tabOtherName = string.IsNullOrEmpty(item.TableName) ? item.TableName : item.TableName + ".";
selectKey.Add($"{tabOtherName}{item.RowKey} as {item.RowName}");
}
var ExportList = _studentStatuService.Query()
.Include(n => n.Payment)
.Include(n => n.Student)
.Include(n => n.Student.Department)
.Where(n => (int)n.StudentType > )
.HasWhere(request.Where.DepartMentID, n => n.Student.DepartmentID == request.Where.DepartMentID)
.HasWhere(request.Where.EduMajorID, n => n.Payment.EduMajorId == request.Where.EduMajorID)
.HasWhere(request.Where.EduSchoolID, n => n.Payment.EduSchoolId == request.Where.EduSchoolID)
.HasWhere(request.Where.Name, n => n.StudentNo.Contains(request.Where.Name))
.HasWhere(request.Where.IdCard, n => n.Student.IDCard.Contains(request.Where.IdCard))
.HasWhere(request.Where.StartTime, n => n.CreateTime>= request.Where.StartTime)
.HasWhere(request.Where.EndTime, n => n.CreateTime < request.Where.EndTime)
.Select(n => new StudentLessResponse
{
PhoneNum = n.Student.PhoneNum,
School = n.Payment.EduSchoolName,
Major = n.Payment.EduMajorName,
CreateTime = n.CreateTime.ToString("yyyy-MM-dd HH:mm:ss"),
Guid = n.Student.Guid.ToString(),
Name = n.Student.Name,
DeptName = n.Student.Department.Name,
StudentType = (int)n.StudentType,
DiplomaType = string.Join("/", EnumHelper.GetDescription(n.DiplomaType)),
GetSeason = EnumHelper.GetDescription(n.GetSeason),
GetYear = n.GetYear,
LevelSeason = EnumHelper.GetDescription(n.LevelSeason),
LevelYear = n.LevelYear,
StudentNo = n.StudentNo,
TemporaryNo = n.TemporaryNo
})
.Select($"new ({string.Join(',', selectKey.ToArray())})")
.ToDynamicList();
var pack = ExcelHelper<dynamic, StudentPagesRequest>.Export(request, ExportList, LocalPath);
如果需要用到Inner Join
则需要使用SelectMany,具体语法自行百度。
前端代码
Export.js组件:
import React from 'react';
import { Popover, Button, Checkbox, Row, Col, DatePicker } from 'antd';
import PropTypes from 'prop-types';
import { setLocalStorage, getLocalStorage } from '../../utils/cookieHelper';
const { RangePicker } = DatePicker; class Export extends React.Component { state = {
visible: false,
checkedValues: getLocalStorage(this.props.moduleName),
StartTime: null,
EndTime: null
}
onChange = (checkedValues) => {
const { exportList } = this.props; this.setState({ checkedValues });
}
export = () => {
const { checkedValues, EndTime, StartTime } = this.state;
const { moduleName, onExport, exportList } = this.props; setLocalStorage(moduleName, checkedValues);
const finallyValues = exportList.filter((item, index) => {
return checkedValues.indexOf(item.RowKey) > -1;
});
const data = {
ModuleName: moduleName,
RowRequest: finallyValues,
where: {
StartTime,
EndTime
}
}
console.log(data);
if (onExport) {
onExport(data);
} } onTimeChange = (date, dateString) => {
this.setState({ StartTime: dateString[0], EndTime: dateString[1] }) }
render() {
const { exportList, moduleName } = this.props;
const ccontent = (<div> <Checkbox.Group onChange={this.onChange} style={{ width: '220px' }} value={this.state.checkedValues}>
<Row>
<div style={{ marginBottom: '12px' }}>
<div style={{ marginBottom: '5px' }}> 请选择数据产生的时间:</div>
<RangePicker onChange={this.onTimeChange} />
</div> {exportList.map((item, index) => {
return (<Col span={12}><Checkbox key={item.RowKey} value={item.RowKey}>{item.RowName}</Checkbox></Col>)
})}
</Row>
</Checkbox.Group>
<Row style={{ textAlign: 'center', marginTop: '10px' }}>
<Col span={2}></Col>
<Col span={10}> <a onClick={this.export}>确定</a></Col>
<Col span={10}> <a onClick={() => {
let visb = !this.state.visible;
this.setState({ visible: visb })
}}>取消</a></Col>
<Col span={2}></Col> </Row></div>);
return (
<Popover
content={ccontent}
title="请选择导出列"
trigger="click"
visible={this.state.visible}
placement="bottom" >
<Button type="primary" onClick={() => {
let visb = !this.state.visible;
this.setState({ visible: visb })
}} icon={'export'} style={{ marginRight: '10px' }}>导出</Button>
</Popover >
);
}
} Export.propTypes = {
onExport: PropTypes.func,
moduleName: PropTypes.string,
exportList: PropTypes.any
}; export default Export;
使用方式:



最后希望对大家有帮组!
记React+.NetCore API实现动态列导出的更多相关文章
- C# 使用Epplus导出Excel [2]:导出动态列数据
C# 使用Epplus导出Excel [1]:导出固定列数据 C# 使用Epplus导出Excel [2]:导出动态列数据 C# 使用Epplus导出Excel [3]:合并列连续相同数据 C# 使用 ...
- 数据导出Excel,动态列
今天碰到一个需求,要求将用户回答的问卷及问题导出Excel表格,问卷对应的问题数量不一致,需要动态添加列表头,简单记录. 要导出Excel需要添加poi.jar包 用户-问卷实体(固定列): pack ...
- React Router API文档
React Router API文档 一.<BrowserRouter> 使用HTML5历史记录API(pushState,replaceState和popstate事件)的<Rou ...
- vue原生表格怎样实现动态列及表格数据下载
最近项目经常用到带有合并效果以及动态列的表格,而翻阅iview和element-ui官网没有找到合适的(也有可能自己的水平有限,不会改写),所以只好自己用原生表格写了一个,具体效果如下: 这个表格右侧 ...
- 原生JS实战:写了个一边玩游戏,一边记JS的API的游戏
本文是苏福的原创文章,转载请注明出处:苏福CNblog:http://www.cnblogs.com/susufufu/p/5878913.html 本程序[一边玩游戏,一边记JS的API]是本人的个 ...
- Dynamic CRM 2013学习笔记(二十六)报表设计:Reporting Service报表 动态参数、参数多选全选、动态列、动态显示行字体颜色
上次介绍过CRM里开始报表的一些注意事项:Dynamic CRM 2013学习笔记(十五)报表入门.开发工具及注意事项,本文继续介绍报表里的一些动态效果:动态显示参数,参数是从数据库里查询出来的:参数 ...
- 用JSON-server模拟REST API(二) 动态数据
用JSON-server模拟REST API(二) 动态数据 上一篇演示了如何安装并运行 json server , 在这里将使用第三方库让模拟的数据更加丰满和实用. 目录: 使用动态数据 为什么选择 ...
- extjs动态树 动态grid 动态列
由于项目需要做一个动态的extjs树.列等等,简而言之,就是一个都是动态的加载功能, 自己琢磨了半天,查各种资料,弄了将近两个星期,终于做出来了 首先,想看表结构,我的这个功能需要主从两张表来支持 代 ...
- displaytag 动态列实现
这种动态列的实现方法来自displaytag-examples-1.2.war提供的示例中,实际上下载下来的zip文件中不仅有各种jar包,还有这个包含各种例子的war包,是学习displaytag的 ...
随机推荐
- Android基础_BroadcastReceiver
一:什么是BroadcastReceiver Broadcast(广播)是一种广泛运用于在应用程序之间一步传播消息的机制系统消息Android系统发出的,电池不足.来电信息等自定义消息第三方应用发出的 ...
- python爬虫之requests模块介绍
介绍 #介绍:使用requests可以模拟浏览器的请求,比起之前用到的urllib,requests模块的api更加便捷(本质就是封装了urllib3) #注意:requests库发送请求将网页内容下 ...
- 洛谷 P1471 方差
洛谷 P1471 方差 题目背景 滚粗了的HansBug在收拾旧数学书,然而他发现了什么奇妙的东西. 题目描述 蒟蒻HansBug在一本数学书里面发现了一个神奇的数列,包含N个实数.他想算算这个数列的 ...
- 浅析ASCII、Unicode和UTF-8三种常见字符编码
什么是字符编码? 计算机只能处理数字,如果要处理文本,就必须先把文本转换为数字才能处理.最早的计算机在设计时采用8个比特(bit)作为一个字节(byte),所以,一个字节能表示的最大的整数就是255( ...
- 51Nod 1003 阶乘后面0的数量(数学,思维题)
1003 阶乘后面0的数量 基准时间限制:1 秒 空间限制:131072 KB 分值: 5 难度:1级算法题 n的阶乘后面有多少个0? 6的阶乘 = 1*2*3*4*5*6 = 720 ...
- [ZOJ3494]BCD Code
AC自动机+数位DP. 大致题意: BCD码就是把一个数十进制下的每一位分别用4位的二进制表示. 给你一坨01串,问你在一个区间内,有多少个数的BCD码不包含任何一个字符串. 因为涉及到多个串的匹配问 ...
- 最小生成树&最短路基础算法总结
[最短路问题] 解决最短路问题有以下算法:Dijkstra算法,Bellman-Ford算法,Floyd算法,和SPFA算法和启发式搜索算法A*; 每个算法都有它的特点可以解决某些特定的问题,例如:F ...
- c++(爬楼梯)
前两天上网的时候看到一个特别有意思的题目,在这里和朋友们分享一下: 有一个人准备开始爬楼梯,假设楼梯有n个,这个人只允许一次爬一个楼梯或者一次爬两个楼梯,请问有多少种爬法? 在揭晓答案之前,朋友们可以 ...
- Android Studio解决导入项目非常慢的问题
http://www.androidchina.net/5527.html Android Studio比Eclipse ADT有巨大的优势. Android Studio原生支持使用Gradle来构 ...
- vue实现侧边栏手风琴效果
模板 代码如下 html <template> <div class="header"> <ul> <!-- 循环数据在点击调用chang ...