业务需求
导入excel表单,导入成功后预览表单数据。根据表单首行字段,动态生成预览表单的表头。
excel表单格式:
技术栈
vue,ts,elementUI
实现
页面布局,调用接口获取到excel表格中数据,存入 fields 中。
<el-table :data="tableList">
<el-table-column label="行号" width="100">
<template slot-scope="scope">
<div>{{scope.row.index}}</div>
</template>
</el-table-column>
<el-table-column v-for="(item, index) in fields" :key="index" :label="item.label">
<template slot-scope="scope">
<div>{{scope.row[item.field]}}</div>
</template>
</el-table-column>
</el-table>
逻辑实现
1、上传的表格中数据都是后端返回给前端的,这里是调用了getFileData 接口,返回值中 mapList 是个对象数组。
getFileData(params: any) {
MyForm.getFileData(params).then((res) => {
if (res.status === 0) {
this.setTableList(res.data);
} else {
this.$message({
message: res.data.msg,
type: "error",
});
}
});
},
2、setTableList 方法就是动态生成表头数据。
但在此之前,需要明白前后端传值中后端需要的参数格式,这里表单的后端实现逻辑中关键的字段有三个:attrName,attrColumn和value。上图中拿到了attrName和value的对应关系:
后端还提供了一个attrName和attrColumn对应关系的接口,在页面加载的时候来调用。
最终生成的表单的数据是attrColumn和value的对应关系,以这样的数据格式传给后端,那就需要前端处理一下数据格式。
interface IField {
label: string;
field: string;
}
// methods
getDetail() {
UserForm.detail(this.id).then((res) => {
if (res.status === 0) {
const list = res.data.formAttrList;
const fields: IField[] = [];
const fieldMap: {[k: string]: IField } = {};
list.forEach((d: any) => {
const attr = {
label: d.formAttrName,
field: d.formAttrColumn,
};
fields.push(attr);
fieldMap[d.formAttrName] = attr;
});
this.fields = fields;
this.fieldMap = fieldMap;
}
});
},
先定义空的数组fields,map对象fieldMap。循环返回值列表,把需要的attrName和attrColumn字段拿出来组成数组fields。以attrName为key,fields的Item为value,组成map对象fieldMap。
赋值好这两个变量之后,就可以实现setTableList 方法。
3、回到最上边,拿到那个mapList的数据,先做一个校验。当mapList存在的时候,取数组mapList中的第一项,检查表头数据是否存在fieldMap中,如果不存在的话给出提示直接return。
if (mapList && mapList.length > 0) {
const item = mapList[0];
let check = true;
Object.keys(item).forEach((key) => {
if (!this.fieldMap[key]) {
check = false;
}
});
if (!check) {
this.$message({
message: "表单格式已变动,请下载最新模版",
type: "warning",
});
return;
}
// 。。。
}
开始组装表单数据。先定义一个数组用来存放最终结果,循环mapList,返回值中的rowNumber数组里的数据是对应着mapList每一项在excel中的行号。
const tableList: any[] = [];
mapList.forEach((d: {[k: string]: any}, index: number) => {
const row: {[k: string]: any} = {
index: rowNumber[index],
};
Object.keys(d).forEach((name) => {
const field = this.fieldMap[name].field;
row[field] = d[name];
});
tableList.push(row);
});
this.tableList = tableList;
整体 setTableList 方法如下:
setTableList(data: any) {
this.tableList = [];
this.errorColumn = "";
const { mapList, rowNumber, errorColumn } = data;
if (mapList && mapList.length > 0) {
const item = mapList[0];
let check = true;
Object.keys(item).forEach((key) => {
if (!this.fieldMap[key]) {
check = false;
}
});
if (!check) {
this.$message({
message: "表单格式已变动,请下载最新模版",
type: "warning",
});
return;
}
// 组装表格数据
const tableList: any[] = [];
mapList.forEach((d: {[k: string]: any}, index: number) => {
const row: {[k: string]: any} = {
index: rowNumber[index],
};
Object.keys(d).forEach((name) => {
const field = this.fieldMap[name].field;
row[field] = d[name];
});
tableList.push(row);
});
this.tableList = tableList;
}
// 错误信息
if (errorColumn) {
errorColumn.forEach((el: string) => {
const row = Number(el.split(",")[0]) + 1;
const column = Number(el.split(",")[1]) + 1;
this.errorColumn = `${row}行${column}列`;
});
}
},
4、最终效果