@sparta-utils/excel-validate-helper
Version:
Excel 读取、校验、错误标注和导出工具库
154 lines (153 loc) • 6.6 kB
JavaScript
const PREDEFINED_PATTERNS = {
idCard: /^\d{15}|\d{18}$/,
phone: /^1[3-9]\d{9}$/,
tel: /^\d{3,4}-?\d{7,8}$/,
email: /^[a-zA-Z0-9._-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,6}$/
};
export class ExcelValidator {
static validate(data, options) {
const errors = [];
const results = [];
const seenUniqueCols = {};
const groupKeyToRowMap = new Map();
const { rules = {}, colHeaders, fieldNames, startRowIndex = 2, onProgress, uniqueGroupCols = [] } = options;
const totalRows = data.length;
for (let rowIndex = 0; rowIndex < totalRows; rowIndex++) {
const row = data[rowIndex];
const resultRow = { rowNumber: row[0] };
const rowErrors = [];
const excelRow = startRowIndex + rowIndex;
for (let colIndex = 0; colIndex < fieldNames.length; colIndex++) {
const header = colHeaders[colIndex] || `第${colIndex + 1}列`;
const field = fieldNames[colIndex];
let val = row[colIndex + 1]; // +1 是跳过 rowNumber
const ruleList = Array.isArray(rules[colIndex])
? rules[colIndex]
: rules[colIndex]
? [rules[colIndex]]
: [];
if (typeof val === 'string')
val = val.trim();
for (const rule of ruleList) {
if (rule.required &&
(val === undefined || val === null || val === '')) {
rowErrors.push({
row: excelRow,
col: colIndex,
reason: `${header}为必填项`
});
continue;
}
if (val === undefined || val === null || val === '')
continue;
if (rule.type === 'number') {
const num = Number(val);
if (isNaN(num)) {
rowErrors.push({
row: excelRow,
col: colIndex,
reason: `${header}应为数字`
});
continue;
}
val = num;
if (rule.min !== undefined && num < rule.min) {
rowErrors.push({
row: excelRow,
col: colIndex,
reason: `${header}不能小于${rule.min}`
});
}
if (rule.max !== undefined && num > rule.max) {
rowErrors.push({
row: excelRow,
col: colIndex,
reason: `${header}不能大于${rule.max}`
});
}
}
if (rule.type === 'boolean') {
if (val !== true && val !== false) {
rowErrors.push({
row: excelRow,
col: colIndex,
reason: `${header}应为布尔值`
});
}
}
if (rule.pattern && !rule.pattern.test(val)) {
rowErrors.push({
row: excelRow,
col: colIndex,
reason: `${header}格式不正确`
});
}
if (rule.custom) {
const customResult = rule.custom(val, row, rowIndex);
if (customResult) {
rowErrors.push({
row: excelRow,
col: colIndex,
reason: `${header}:${customResult}`
});
}
}
if (rule.unique) {
if (!seenUniqueCols[colIndex])
seenUniqueCols[colIndex] = new Set();
if (seenUniqueCols[colIndex].has(val)) {
rowErrors.push({
row: excelRow,
col: colIndex,
reason: `${header}不唯一`
});
}
else {
seenUniqueCols[colIndex].add(val);
}
}
}
resultRow[field] = val;
}
// 联合唯一性校验(增强行号提示)
if (uniqueGroupCols.length > 0) {
const groupValues = uniqueGroupCols.map((i) => {
const val = row[i + 1];
return val !== undefined && val !== null ? String(val).trim() : '';
});
const key = groupValues.join('|');
const isEmpty = groupValues.some((v) => v === '');
if (!isEmpty) {
const existedRow = groupKeyToRowMap.get(key);
if (existedRow !== undefined) {
uniqueGroupCols.forEach((i) => {
rowErrors.push({
row: excelRow,
col: i,
reason: `${colHeaders[i] || `第${i + 1}列`}组合值不唯一,已在第 ${existedRow} 行出现`
});
});
}
else {
groupKeyToRowMap.set(key, excelRow);
}
}
}
if (rowErrors.length > 0) {
errors.push(...rowErrors);
}
else {
results.push(resultRow);
}
if (onProgress && rowIndex % 50 === 0) {
onProgress(rowIndex + 1, totalRows);
}
}
if (onProgress)
onProgress(totalRows, totalRows);
return { err: errors, succ: results };
}
static getPredefinedPattern(name) {
return PREDEFINED_PATTERNS[name];
}
}