Vue3实现动态导入Excel表格数据
1. 前言
在开发工作过程中,我们会遇到各种各样的表格数据导入,大部分我们的解决方案:提供一个模板前端进行下载,然后按照这个模板要求进行数据填充,最后上传导入,这是其中一种解决方案。个人认为还有另外一种解决方案,不一定会前面的方案好,方便,但是可以减少人为操作,减少出错,更为通用,就是进行动态数据导入,前端进行自定义导入数据,配置数据对应关系,最后提交导入结果。
2. 如何实现
2.1使用技术
后台使用.Net6.0,前端使用Vue3
2.2实现思路
- 前端通过Excel表格数据导入,导入后客户可以进行数据编辑(未实现)客户数据确认后进行数据回写主页面
- 设置数据对应关系,也就是后台所需数据格式和前台数据格式进行绑定
- 数据校验,进行数据提交
2.3具体实现方案
2.3.1导入Excel数据
创建列表页面,页面主要布局如下

Table需要绑定的列是未知的,所以el-table绑定的el-table-column是需要动态进行加载,动态定义表格以及变量
<el-table :data="state.tableData.data">
<el-table-column
v-for="item in state.colunm"
:prop="item.key"
:key="item.key"
:label="item.lable"
>
</el-table-column>
</el-table>
const state = reactive({
colunm: [{key: "", lable: ""}],
});
点击导入报名数据,弹出上传数据页面

这一块的功能在之前的一篇文章有写过:https://www.cnblogs.com/wuyongfu/p/16651107.html
子组件的核心代码
页面布局:
<template>
<el-dialog :close-on-click-modal="false" v-model="state.dialogVisible" :title="title" width="70%" >
<el-upload
class="upload-demo"
:auto-upload="false"
:on-change="uploadChange" style="text-align: left;" >
<el-button type="primary">上传文件</el-button>
</el-upload>
<el-form-item>
<el-button @click='submit'>确认导入</el-button>
</el-form-item>
<el-table :data="state.tableData.data" max-height="500px">
<el-table-column v-for="item in state.colunm" :prop="item.key" :key="item.key" :label="item.lable">
</el-table-column>
</el-table>
<div class='block flex justify-end' v-if='state.tableData.total > 0'>
<el-pagination v-model:currentPage="state.searchInput.PageIndex" v-model:page-size="state.searchInput.PageSize"
:page-sizes="[10, 50, 200, 1000]" layout="total, sizes, prev, pager, next, jumper" @size-change="getData"
@current-change="getData" :total="state.tableData.total" />
</div>
</el-dialog>
</template>
变量定义:
const state = reactive({
tempTableData: [{}],//临时存储全部数据
searchInput: { PageIndex: 1, PageSize: 10 },
tableData: { data: [{}], total: 0 },//表格加载当前页面数据
dialogVisible: false,
colunm: [{ key: '', lable: '' }]
});
父页面调用方法,进行表格列头加载,初始化表格数据:
const childMethod = (data) => {
state.colunm = data;
state.tableData.data = [];
state.dialogVisible = true;
}
绑定表格方法,前端进行分页数据处理:(这里可能会有性能问题,暂时没有仔细探究)
const getData = () => {
const tempData: any = [];
state.tempTableData.forEach((value, index) => {
if (index >= ((state.searchInput.PageIndex - 1) * state.searchInput.PageSize) && index < ((state.searchInput.PageIndex) * state.searchInput.PageSize)) {
tempData.push(value);
}
});
state.tableData.data = tempData;
state.tableData.total = state.tempTableData.length;
}
提交数据回写父组件方法
const submit = () => {
console.log(state.tempTableData);
context.emit('childClick', state.tempTableData,state.colunm)
}
上传Excel读取数据方法,主要动态绑定列以及导入的数据
const uploadChange = async (file) => {
let dataBinary = await readFile(file.raw)
let workBook = XLSX.read(dataBinary, { type: 'binary', cellDates: true })
let workSheet = workBook.Sheets[workBook.SheetNames[0]]
let data: any = XLSX.utils.sheet_to_json(workSheet)
let mycolunm={};
Object.setPrototypeOf(mycolunm,data[0]);
state.colunm=[];
for(let key in mycolunm){
state.colunm.push( { lable: key, key: key })
}
let tHeader = state.colunm.map(obj => obj.lable)
let filterVal = state.colunm.map(obj => obj.key)
tHeader.map(val => filterVal.map(obj => val[obj]))
const tempData: any = [];
data.forEach((value) => {
const ob = {};
tHeader.forEach((item, index) => {
ob[filterVal[index]] = value[item].toString();
})
tempData.push(ob);
})
state.tempTableData = tempData;
getData();
}
这里导入数据后,会调用主组件方法(dataUploadchildClick)用于回写主组件表格数据,列头等数据
const dataUploadchildClick = (data, colunm) => {
state.colunm = colunm;
state.tempData = data;
getData();
dataUpload.value.cancel();
};
2.3.2设置数据对应关系
定义后台需要的列
SelectData: [
{key: 'zkzh', type: '', value: '', selectValue: '', title: '准考证号'},
{key: 'zjlb', type: '', value: '', selectValue: '', title: '证件类别'},
{key: 'zjhm', type: '', value: '', selectValue: '', title: '证件号码'},
{key: 'ksxm', type: '', value: '', selectValue: '', title: '考生姓名'},
{key: 'xb', type: '', value: '', selectValue: '', title: '性别'},
{key: 'ss', type: '', value: '', selectValue: '', title: '省市'},
{key: 'kq', type: '', value: '', selectValue: '', title: '考区'},
{key: 'kdh', type: '', value: '', selectValue: '', title: '考点号'},
{key: 'kdmc', type: '', value: '', selectValue: '', title: '考点名称'},
{key: 'kch', type: '', value: '', selectValue: '', title: '考场号'},
{key: 'kchdz', type: '', value: '', selectValue: '', title: '考场地址'},
{key: 'zwh', type: '', value: '', selectValue: '', title: '座位号'},
{key: 'chc', type: '', value: '', selectValue: '', title: '场次'}
]
创建Select组件,组件核心代码
页面布局部分
<label style="width: 50px;display: inline-block;">{{ itemKey }}:</label>
<el-select v-model="state.type" :placeholder="name" size="large" clearable>
<el-option
v-for="item in state.options"
:key="item.value"
:label="item.label"
:value="item.value"
/>
</el-select>
<el-input v-model="state.value" style="width: 200px;" :placeholder="name" size="large" v-if="state.type=='0'"/>
<el-select v-model="state.selectValue" style="width: 200px;" :placeholder="name" size="large" clearable
v-if="state.type=='1'||state.type=='2'">
<el-option
v-for="item in state.ValueOptions"
:key="item.key"
:label="item.lable"
:value="item.key"
/>
</el-select>
接受参数定义
props: {
itemKey: String,
name: String,
type: String,
value: String,
selectValue: String,
colunm: Object
},
页面变量定义,这里定义的默认option主要有三个
- 0 固定值,这一列为固定值,不从表格中读取
- 1 下拉框,从导入数据中选择
- 2 自动生成,这里主要是特殊业务,可以进行自定义扩展
const state = reactive({
value: ref(''),
type: ref(''),
selectValue: ref([]),
ValueOptions:[{}],
options: [
{
value: '0',
label: '固定值',
},
{
value: '1',
label: '下拉框',
},
{
value: '2',
label: '自动生成',
},
],
});
监听下拉框选择类型:
watch(() => state.type, (newVal) => {
context.emit('update:type', newVal);
}
);
监听下拉框选择固定,文本框输入的值:
watch(() => state.value, (newVal) => {
context.emit('update:value', newVal)
})
监听下拉框选择下拉框,后面下拉框选择的值:
watch(() => state.selectValue, (newVal) => {
context.emit('update:selectValue', newVal)
})
监听表格的列,动态加载下拉框绑定的值:
watch(() => props.colunm, (newVal: any) => {
state.ValueOptions=newVal;
})
最终的效果:

父页面进行引用
<el-row :gutter="24" justify="start" style="text-align: left;">
<div v-for="(item,index) in state.SelectData" :key='index' style="margin-top: 5px;width: 100%;">
<el-col :span="24"><Select
:itemKey="item.key"
v-model:type="item.type"
v-model:value="item.value"
v-model:selectValue="item.selectValue"
:name="item.title"
:colunm="state.colunm"/></el-col>
</div>
</el-row>
2.3.3进行数据提交
提交数据到后台进行处理,这里根据自己的业务进行验证,或则进行其它扩展
const Save = () => {
if (state.tempData.length == 0) {
state.active=1;
ElMessage.warning('请导入考生数据');
return;
}
let CheckSelectData = true;
state.SelectData.forEach((value, index) => {
if (!value.type) {
CheckSelectData = false
}
});
if (!CheckSelectData) {
state.active=2;
ElMessage.warning('请设置完成数据对应关系');
return;
}
if (state.tableTimeData.data.length==0){
state.active=3;
ElMessage.warning('请添加场次数据');
return;
}
if (!state.ExamData.jiancheng||!state.ExamData.kaikaonianyue||!state.ExamData.quancheng){
state.active=4;
ElMessage.warning('请设置任务相关信息');
return;
}
axios.post('/GenerateCheckTemplate', state)
.then(function (response) {
if (response.status == 200) {
ElMessage.success(response.data);
dataUpload.value.cancel();
getData();
} else {
ElMessage.error(response.data)
}
})
}
后台定义接口:/GenerateCheckTemplate
定义实体对前台导入数据进行接收:
public class GenerateCheckTemplate
{
/// <summary>
/// 考试任务对象
/// </summary>
public ExamData ExamData { get; set; } /// <summary>
/// 数据对应关系对象
/// </summary>
public List<Correspondence> SelectData { get; set; } /// <summary>
/// 导入数据
/// </summary>
public List<Dictionary<string, string>> tempData { get; set; } /// <summary>
/// 导入数据列集合
/// </summary>
public List<Colunm> colunm { get; set; } /// <summary>
/// 场次对象
/// </summary>
public tableTimeData tableTimeData { get; set; }
} /// <summary>
/// 考试任务对象
/// </summary>
public class ExamData
{
/// <summary>
/// 简称
/// </summary>
public string jiancheng { get; set; }
/// <summary>
/// 考试年月
/// </summary>
public string kaikaonianyue { get; set; }
/// <summary>
/// 简称
/// </summary>
public string quancheng { get; set; }
}
/// <summary>
/// 数据对应关系对象
/// </summary>
public class Correspondence
{
/// <summary>
/// key
/// </summary>
public string key { get; set; } /// <summary>
/// 下拉选择数据
/// </summary>
public string selectValue { get; set; } /// <summary>
/// 标题
/// </summary>
public string title { get; set; } /// <summary>
/// 类型 0 固定值 1下拉框选择
/// </summary>
public string type { get; set; } /// <summary>
/// 固定值数据
/// </summary>
public string value { get; set; }
} /// <summary>
/// 表格对象
/// </summary>
public class tableTimeData
{
/// <summary>
/// 提交表格数据
/// </summary>
public List<ExamTime> data { get; set; }
/// <summary>
/// 总数
/// </summary>
public int total { get; set; }
} public class ExamTime
{
/// <summary>
/// 场次编码
/// </summary>
public int? ExamTimeCode { get; set; }
/// <summary>
/// 场次名称
/// </summary> public string ExamTimeName { get; set; } /// <summary>
/// 开始时间
/// </summary>
public string startTime { get; set; }
/// <summary>
/// 结束时间
/// </summary>
public string endTime { get; set; } /// <summary>
/// 是否编辑状态
/// </summary>
public bool Edit { get; set; }
}
/// <summary>
/// 导入数据列对象
/// </summary>
public class Colunm
{
/// <summary>
/// 键值
/// </summary>
public string key { get; set; }
/// <summary>
/// 值
/// </summary>
public string lable { get; set; }
}
然后处理前端传过来的数据转换成需要的数据,这里type需要修改成枚举类型,类型可以根据需求进行扩展
/// <summary>
/// 处理前端传过来的数据
/// </summary>
/// <param name="companiesInput"></param>
private void ProcessingData(GenerateCheckTemplate companiesInput, DataTable dataTable)
{
Dictionary<string, string> keyValuePairs = new Dictionary<string, string>();
foreach (var data in companiesInput.tempData)
{
DataRow dataRow = dataTable.NewRow(); foreach (Correspondence correspondence in companiesInput.SelectData)
{
if (correspondence.type == "0")
{
dataRow[correspondence.key] = correspondence.value;
}
else if (correspondence.type == "1")
{
dataRow[correspondence.key] = data[correspondence.selectValue];
}
else if (correspondence.type == "2")
{
string value = data[correspondence.selectValue];
if (keyValuePairs.ContainsKey(value))
{
dataRow[correspondence.key] = keyValuePairs[value];
}
else
{
keyValuePairs.Add(value, (keyValuePairs.Count + 1).ToString().PadLeft(2, '0'));
dataRow[correspondence.key] = keyValuePairs[value];
}
}
} dataTable.Rows.Add(dataRow);
}
}
整体的功能到这里基本上实现了,具体的细节可能需要根据不同的项目进行优化,有更多的方案可以一起进行交流
附上源码地址:https://gitee.com/wyf854861085/file-upload.git
Vue3实现动态导入Excel表格数据的更多相关文章
- java导入Excel表格数据
首先导入Excel数据需要几样东西 第一需要两个依赖包,这里直接是在pom注入依赖 <!--excel--> <dependency> <groupId>org.a ...
- tp3.2和Bootstrap模态框导入excel表格数据
导入按钮 <button class="btn btn-info" type="button" id="import" data-to ...
- poi导入excel表格数据到数据库的时候,对出生日期的校验
出生日期格式为8位数字的字符串 如:yyyyMMdd 规则:yyyy大于1900并小于当前时间,月.日 按日期规则校验 //解决读过来的字符串显示为科学计数法问题 BigDecimal bd = ne ...
- 用多线程优化Excel表格数据导入校验的接口
公司的需求,当前某个Excel导入功能,流程是:读取Excel数据,传入后台校验每一条数据,判断是否符合导入要求,返回给前端,导入预览展示.(前端等待响应,难点).用户再点击导入按钮,进行异步导入(前 ...
- 将包含经纬度点位信息的Excel表格数据导入到ArcMap中并输出成shapefile
将包含经纬信息的Excel表格数据,导入到ArcMap中并输出成shapefile,再进行后面的操作.使用这种方法可以将每一个包含经纬信息的数据在ArcMap中点出来. 一.准备数据 新建Excel表 ...
- Excel表格数据导入MySQL数据库
有时候项目需要将存在表格中的批量数据导入数据库,最近自己正好碰到了,总结一下: 1.将excel表格另存为.csv格式文件,excel本身的.xlsx格式导入时可能会报错,为了避免不必要的格式错误,直 ...
- C#调用NPOI组件读取excel表格数据转为datatable写入word表格中并向word中插入图片/文字/书签 获得书签列表
调用word的com组件将400条数据导入word表格中耗时10分钟简直不能忍受,使用NPOI组件耗时4秒钟.但是NPOI中替换书签内容的功能不知道是不支持还是没找到. 辅助类 Excel表格数据与D ...
- springMVC(5)---导入excel文件数据到数据库
springMVC(5)---导入excel文件数据到数据库 上一篇文章写了从数据库导出数据到excel文件,这篇文章悄悄相反,写的是导入excel文件数据到数据库.上一篇链接:springMVC(4 ...
- jxl读取Excel表格数据
调用jxl包实现Excel表格数据的读取,代码如下: import java.io.File; import java.io.IOException; import java.util.ArrayLi ...
随机推荐
- 【JDBC】学习路径1-JDBC背景知识
学习完本系列JDBC课程后,你就可以愉快使用Java操作我们的MySQL数据库了. 各种数据分析都不在话下了. 第一章:废话 JDBC编程,就是写Java的时候,调用了数据库. Java Databa ...
- 业务流程可视化-让你的流程图"Run"起来(7.运行状态持久化&轻量工作流支持)
前言 感谢大家阅读本项目系列文章和对项目的支持.分享一下我对这个项目的新的改进. 之前项目做到了流程设计可视化和流程运行结果可视化. 本期发布的版本中实现了中间的运行过程的实时可视化,和流程状态持久化 ...
- Java控制台打印三角形
for (int i = 1; i <= 5; i++) { //最上面先是五个往下一次4.3.2.1 for (int j = 5; j >= i; j--) { System.out. ...
- Html飞机大战(二):面向对象绘制背景
好家伙, 我们为了后续工作的顺利进行,我试着把每一个模块封装为对象 但冻手之前还是要构思一下 我们把天空封装成一个类: 1.来搞一手简单的对象分析: 属性方面的都好理解 来说明一下方法: (1) p ...
- 第五十三篇:Vue安装Element ui
好家伙,之前写的一篇过时了,用不了了,更新一波 (已新建一个vue项目) 1. 在项目目录下执行:npm i element-ui -S 2. 在main.js中写入 import ElementUI ...
- KingbaseES V8R6集群部署案例之---Windows环境配置主备流复制(异机复制)
案例说明: 目前KingbaseES V8R6的Windows版本不支持数据库sys_rman的物理备份,可以考虑通过建立主备流复制实现数据库的异机物理备份.本案例详细介绍了,在Windows环境下建 ...
- KingbaseES R6 集群修改物理IP和VIP案例
在用户的实际环境里,可能有时需要修改主机的IP,这就涉及到集群的配置修改.以下以例子的方式,介绍下KingbaseES R6集群如何修改IP. 一.案例测试环境 操作系统: [KINGBASE@nod ...
- alter role 导致的数据库无法登录问题
ALTER ROLE 用于更改一个数据库角色.只要改角色后续开始一个新会话,指定的值将会成为该会话的默认值,并且会覆盖 kingbase.conf中存在的值或者从命令行收到的值. 显性的更改角色的一 ...
- Springboot pageHelper总结
Springboot上使用pageHelper总结 1. 在pom.xml 中引入依赖 <!--pagehelper--> <dependency> <groupId&g ...
- [Python]-pydicom模块处理DICOM数据
在处理医疗数据时,经常要跟DICOM文件打交道.在使用Python处理时,不得不提常用的pydicom模块. import pydicom DICOM文件读取 pydicom.read_file()读 ...