UNPKG

@sparta-utils/excel-validate-helper

Version:

Excel 读取、校验、错误标注和导出工具库

154 lines (153 loc) 6.6 kB
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]; } }