项目中,多次使用到了读取表格。考虑到后续可能会修改表头(新增必填标志,或者删除必填标志,在后面加(***)注解什么的),不改变主体部分只校验的主体部分,根据自己手动传值判断第几行是表头
1.安装插件
npm install xlsx
2.封装xlsx读取部分代码 vueExcelHandler.js
import * as XLSX from 'xlsx'; // 确保你已经安装了 xlsx 库
// 清洗表头字符串
function cleanHeader(header) {
// 去除所有空格
let cleanedHeader = header.replace(/\s+/g, '');
// 去除星号
cleanedHeader = cleanedHeader.replace(/\*+/g, '');
// 使用循环确保去除所有的括号及其内容
while (cleanedHeader.includes('(') && cleanedHeader.includes(')')) {
cleanedHeader = cleanedHeader.replace(/\(.*?\)/g, '');
}
while (cleanedHeader.includes('(') && cleanedHeader.includes(')')) {
cleanedHeader = cleanedHeader.replace(/(.*?)/g, '');
}
// 转换为小写
return cleanedHeader.toLowerCase();
}
function validateSheetHeaders(worksheet, requiredColumns,headerRowNumber) {
// const headerRow = XLSX.utils.sheet_to_json(worksheet, { header: headerRowNumber })[0];
// 从指定行获取表头
const headerRow = getRow(worksheet, headerRowNumber-1);
console.log('原始表头:', headerRow);
const cleanedHeaderRow = headerRow.map(cleanHeader);
console.log('清洗后的表头:', cleanedHeaderRow);
const requiredColumnsClean = requiredColumns.map(cleanHeader);
console.log('必需的列(清洗后):', requiredColumnsClean);
// 创建一个映射关系,用于检查必需的列是否全部存在
const headerMap = new Map(cleanedHeaderRow.map((col, index) => [col, index]));
// 检查必需的列是否全部存在
const missingColumns = requiredColumnsClean.filter(col => !headerMap.has(col));
if (missingColumns.length > 0) {
console.error(`缺少以下必需的列:${missingColumns.join(', ')}`);
return false;
}
return true;
}
function getRow(worksheet, rowIndex) {
const range = XLSX.utils.decode_range(worksheet['!ref']);
const row = [];
for (let C = range.s.c; C <= range.e.c; ++C) {
const cell = worksheet[XLSX.utils.encode_cell({ c: C, r: rowIndex })];
row.push(cell ? XLSX.utils.format_cell(cell) : '');
}
return row;
}
// 文件读取和处理函数
function processExcelFile(file, config,headerd) {
const requiredColumns = config.requiredColumns;
const fieldMap = config.fieldMap || {};
const callback = config.callback;
const headerRowNumber = config.headerRowNumber || 1; // 默认表头在第1行
const reader = new FileReader();
reader.onload = (e) => {
const data = e.target.result;
let workbook;
if (typeof data === 'string') { // 对于旧版xlsx,可能直接是二进制字符串
workbook = XLSX.read(data, {type: 'binary'});
} else { // 对于新版xlsx,通常为ArrayBuffer
workbook = XLSX.read(new Uint8Array(data), {type: 'array'});
}
// 获取第一个工作表
const worksheet = workbook.Sheets[workbook.SheetNames[0]];
console.log(worksheet)
// 验证表头是否符合要求
if (!validateSheetHeaders(worksheet, requiredColumns,headerRowNumber)) {
callback(null, '表头不符合要求');
return;
}
// 读取指定行作为表头
const actualHeaderRow = getRow(worksheet, headerRowNumber-1);
const cleanedActualHeaderRow = actualHeaderRow.map(cleanHeader);
console.log('实际使用的表头(清洗后):', cleanedActualHeaderRow);
// 动态构建fieldMap
const dynamicFieldMap = {};
requiredColumns.forEach((col, index) => {
const cleanedCol = cleanHeader(col);
if (cleanedActualHeaderRow.includes(cleanedCol)) {
dynamicFieldMap[cleanedCol] = fieldMap[cleanedCol] || cleanedCol;
}
});
console.log(requiredColumns)
// 获取数据行的范围
const range = XLSX.utils.decode_range(worksheet['!ref']);
const startRow = range.s.r + headerRowNumber; // 数据从表头之后的第一行开始
const endRow = range.e.r;
// 读取数据行
let excelData = [];
console.log(cleanedActualHeaderRow)
for (let R = startRow; R <= endRow; ++R) {
const row = {};
cleanedActualHeaderRow.forEach((cleanedKey, index) => {
// 编码单元格的位置
const cellRef = XLSX.utils.encode_cell({ c: index, r: R });
const cell = worksheet[cellRef];
if (cell && cell.t) {
row[dynamicFieldMap[cleanedKey] || cleanedKey] = XLSX.utils.format_cell(cell);
}
});
excelData.push(row);
}
// 过滤掉空白行
excelData = excelData.filter(row => Object.values(row).some(value => value !== ''));
// 执行回调函数
callback(excelData);
};
reader.readAsArrayBuffer(file); // 读取为ArrayBuffer
}
export { processExcelFile };
2.多个表头需要校验,也一起封装起来,避免后面修改,多次改页面造成失误 mainBody.js
export default class ConfigManager {
constructor() {
this.configs = {
config1: {
requiredColumns: ['姓名', '年龄', '性别', '职位'],
fieldMap: {
//对应后端字段
'姓名': 'name',
'年龄': 'age',
'性别': 'gender',
'职位': 'position'
},
callback: (excelData) => {
console.log('Config 1:', excelData);
}
},
config2: {
requiredColumns: ['姓名', '年龄', '性别', '职位'],
fieldMap: {
'姓名': 'name',
'年龄': 'age',
'性别': 'gender',
'职位': 'position'
},
callback: (excelData) => {
console.log('Config 2:', excelData);
}
},
// 可以添加更多的配置...
};
}
getConfig(configName) {
if (this.configs.hasOwnProperty(configName)) {
return this.configs[configName];
} else {
throw new Error(`Configuration '${configName}' not found.`);
}
}
}
3.使用
import { processExcelFile } from '@/你的路径/vueExcelHandler.js';
import ConfigManager from '@/你的路径/mainBody.js';
上传框
<input type="file" @change="handleFileUpload" name="" ref="fjset">
handleFileUpload(event) {
const files = event.target.files || event.dataTransfer.files;
if (files.length > 0) {
const file = files[0];
const imgType = file.type === "application/vnd.ms-excel";
const isLt10M = file.size / 1024 / 1024 < 10;
if (!isLt10M) {
alert("文件大小不能大于10M!");
this.$refs['fjset'].value = '';
return;
}
// 选择需要的配置名称
const configName = 'config1'; // 或者 'config2'
try {
const config = this.configManager.getConfig(configName);
config.headerRowNumber =2; // 假设表头在第2行
config.callback = (excelData, error) => {
this.$refs['fjset'].value = ''; //清空输入框
if (error) {
alert(error+1);
} else {
this.tableData = excelData;
console.log(excelData);
}
};
processExcelFile(file, config);
} catch (error) {
alert(error.message);
this.$refs['fjset'].value = ''; // 配置加载错误时清空输入框
}
}
}