UNPKG

e-virt-table

Version:

A powerful data table based on canvas. You can use it as data grid、Microsoft Excel or Google sheets. It supports virtual scroll、cell edit etc.

448 lines 14.6 kB
function generateShortUUID() { return 'xxxxxxxxxxxxxxxxxx'.replace(/[x]/g, function (c) { const r = (Math.random() * 16) | 0; const v = c === 'x' ? r : (r & 0x3) | 0x8; return v.toString(16); }); } function throttle(func, delay) { let lastCalledTime = 0; let timeoutId; return function (...args) { const now = new Date().getTime(); const elapsedTime = now - lastCalledTime; const wait = typeof delay === 'function' ? delay() : delay; if (!lastCalledTime || elapsedTime >= wait) { func.apply(this, args); lastCalledTime = now; } else if (!timeoutId) { timeoutId = setTimeout(() => { func.apply(this, args); lastCalledTime = new Date().getTime(); timeoutId = undefined; }, wait - elapsedTime); } return undefined; }; } /** * 根据children获取最大深度 * @param data * @returns */ function getMaxRow(data = []) { if (data.length) { return data.map((item) => getMaxRow(item.children) + 1).sort((a, b) => b - a)[0]; } return 0; } function sortFixed(arr = []) { let lefts = []; let centers = []; let right = []; arr.forEach((item) => { if (item.fixed === 'left') { lefts.push(item); } else if (item.fixed === 'right') { right.push(item); } else { centers.push(item); } }); return [ ...lefts.sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0)), ...centers.sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0)), ...right.sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0)), ]; } function calCrossSpan(arr = [], maxRow = 1, level = 0, parentKey = '') { return arr .sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0)) .map((config) => { if (config.children) { let colspan = 0; let fixed = config.fixed; config.children.forEach((item) => { item.fixed = fixed; }); const children = calCrossSpan(config.children, maxRow - 1, level + 1, config.key); if (children) { children.forEach((item) => { colspan += item.colspan ?? 0; }); } return { ...config, width: config.width, level, rowspan: 1, colspan, parentKey, children: children.sort((a, b) => (a.sort ?? 0) - (b.sort ?? 0)), }; } return { ...config, level, rowspan: maxRow, colspan: 1, parentKey, }; }); } function toLeaf(arr = []) { let tmp = []; arr.forEach((item) => { if (item.children) { tmp = tmp.concat(toLeaf(item.children)); } else { tmp.push(item); } }); return tmp; } function filterHiddenColumns(columns) { return columns .filter((col) => !col.hide) // 先过滤掉自己 hide 的列 .map((col) => { // 如果有子列 if (Array.isArray(col.children) && col.children.length > 0) { return { ...col, children: filterHiddenColumns(col.children), // 递归处理 }; } return { ...col }; }); } function debounce(func, delay) { let timeoutId = null; return function (...args) { if (timeoutId !== null) { clearTimeout(timeoutId); } timeoutId = setTimeout(() => { func(...args); }, delay); }; } const regUniversalNewLine = /^(\r\n|\n\r|\r|\n)/; const regNextCellNoQuotes = /^[^\t\r\n]+/; const regNextEmptyCell = /^\t/; /** * @decodeSpreadsheetStr * @desc Decode spreadsheet string into array. refer from http://github.com/warpech/sheetclip/ * @param {string} str The string to parse. * @returns {array} */ function decodeSpreadsheetStr(str) { let arr = [['']]; if (str.length === 0) { return arr; } let column = 0; let row = 0; let lastLength; while (str.length > 0) { if (lastLength === str.length) { // In the case If in last cycle we didn't match anything, we have to leave the infinite loop break; } lastLength = str.length; if (str.match(regNextEmptyCell)) { str = str.replace(regNextEmptyCell, ''); column += 1; arr[row][column] = ''; } else if (str.match(regUniversalNewLine)) { str = str.replace(regUniversalNewLine, ''); column = 0; row += 1; arr[row] = ['']; } else { let nextCell = ''; if (str.startsWith('"')) { let quoteNo = 0; let isStillCell = true; while (isStillCell) { const nextChar = str.slice(0, 1); if (nextChar === '"') { quoteNo += 1; } nextCell += nextChar; str = str.slice(1); if (str.length === 0 || (str.match(/^[\t\r\n]/) && quoteNo % 2 === 0)) { isStillCell = false; } } nextCell = nextCell .replace(/^"/, '') .replace(/"$/, '') .replace(/["]*/g, (match) => new Array(Math.floor(match.length / 2)).fill('"').join('')); } else { const matchedText = str.match(regNextCellNoQuotes); nextCell = matchedText ? matchedText[0] : ''; str = str.slice(nextCell.length); } arr[row][column] = nextCell; } } // 去除 excel 最后一个多余的换行数据 if (Array.isArray(arr) && arr.length > 1) { if (arr[arr.length - 1].length === 1 && arr[arr.length - 1][0] === '') { arr = arr.slice(0, arr.length - 1); } } return arr; } /** * @decodeSpreadsheetStr * @desc encode array to spreadsheet string. refer from http://github.com/warpech/sheetclip/ * @param {array} str The string to parse. * @returns {string} */ function encodeToSpreadsheetStr(arr) { let r; let rLen; let c; let cLen; let str = ''; let val; for (r = 0, rLen = arr.length; r < rLen; r += 1) { cLen = arr[r].length; for (c = 0; c < cLen; c += 1) { if (c > 0) { str += '\t'; } val = arr[r][c]; if (typeof val === 'string') { if (val.indexOf('\n') > -1) { str += `"${val.replace(/"/g, '""')}"`; } else { str += val; } } else if (val === null || val === void 0) { // void 0 resolves to undefined str += ''; } else { str += val; } } if (r !== rLen - 1) { str += '\n'; } } return str; } // 获取合并单元格的spanArr,针对行数据相同key合并 function getSpanArrByRow(list, key, relationRowKeys = []) { let contactDot = 0; const spanArr = []; list.forEach((item, index) => { if (index === 0) { spanArr.push(1); } else { const curValue = relationRowKeys.reduce((acc, key) => `${acc}${item[key] ?? ''}`, '') || item[key]; const pValue = relationRowKeys.reduce((acc, key) => `${acc}${list[index - 1][key] ?? ''}`, '') || list[index - 1][key]; if (curValue === pValue) { spanArr[contactDot] += 1; spanArr.push(0); } else { spanArr.push(1); contactDot = index; } } }); return spanArr; } function getSpanObjByColumn(row, columns) { let keyPre = ''; let keyDot = ''; const spanObj = {}; columns.forEach((item, index) => { if (index === 0) { keyPre = item.key; keyDot = item.key; spanObj[item.key] = 1; } else { // eslint-disable-next-line no-undef if (row[item.key] === row[keyPre]) { spanObj[item.key] = 0; spanObj[keyDot] += 1; } else { spanObj[item.key] = 1; keyPre = item.key; keyDot = item.key; } } }); return spanObj; } // 合并行单元格 function mergeRowCell(params, mergeRowkey, relationRowKeys = []) { // 合并单元格 const { visibleRows, rowIndex, headIndex } = params; const spanArr = getSpanArrByRow(visibleRows, mergeRowkey, relationRowKeys); if (spanArr[rowIndex - headIndex] === 0) { return { rowspan: 0, colspan: 0, relationRowKeys, mergeRow: true, }; } return { rowspan: spanArr[rowIndex - headIndex], colspan: 1, relationRowKeys, mergeRow: true, }; } function mergeColCell(params, mergeColKeys = []) { const { column, row, visibleLeafColumns } = params; const columns = visibleLeafColumns.filter((item) => mergeColKeys.includes(item.key)); // 合并动态列单元格 if (mergeColKeys.includes(column.key)) { const spanObj = getSpanObjByColumn(row, columns); if (spanObj[column.key] === 0) { return { rowspan: 0, colspan: 0, relationColKeys: mergeColKeys, mergeCol: true, }; } return { rowspan: 1, colspan: spanObj[column.key], relationColKeys: mergeColKeys, mergeCol: true, }; } } /** * 获取某个 CSS 变量的实际值 * @param name 变量名(可以带或不带前缀,例如 "color-primary" 或 "--color-primary") * @param el 可选元素,默认为 document.documentElement * @returns CSS 变量的计算值(如 "#1e90ff") */ function getCssVar(name, el = document.documentElement) { const key = name.startsWith('--') ? name : `--${name}`; const styles = getComputedStyle(el); return styles.getPropertyValue(key).trim(); } /** * 解析日期字符串,支持多种常见格式 * @param dateStr 日期字符串 * @returns Date 对象 */ function parseDate(dateStr) { if (!dateStr) return new Date(0); // 如果是数字(时间戳),直接创建Date对象 if (typeof dateStr === 'number') { return new Date(dateStr); } const str = String(dateStr).trim(); // 尝试直接解析 const directDate = new Date(str); if (!isNaN(directDate.getTime())) { return directDate; } // 支持多种常见格式 const patterns = [ // YYYY-MM-DD /^(\d{4})-(\d{1,2})-(\d{1,2})$/, // YYYY/MM/DD /^(\d{4})\/(\d{1,2})\/(\d{1,2})$/, // YYYY.MM.DD /^(\d{4})\.(\d{1,2})\.(\d{1,2})$/, // DD-MM-YYYY /^(\d{1,2})-(\d{1,2})-(\d{4})$/, // DD/MM/YYYY /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/, // DD.MM.YYYY /^(\d{1,2})\.(\d{1,2})\.(\d{4})$/, // MM-DD-YYYY /^(\d{1,2})-(\d{1,2})-(\d{4})$/, // MM/DD/YYYY /^(\d{1,2})\/(\d{1,2})\/(\d{4})$/, // MM.DD.YYYY /^(\d{1,2})\.(\d{1,2})\.(\d{4})$/, // YYYYMMDD /^(\d{4})(\d{2})(\d{2})$/, // 带时间的格式 YYYY-MM-DD HH:mm:ss /^(\d{4})-(\d{1,2})-(\d{1,2})\s+(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?$/, // 带时间的格式 YYYY/MM/DD HH:mm:ss /^(\d{4})\/(\d{1,2})\/(\d{1,2})\s+(\d{1,2}):(\d{1,2})(?::(\d{1,2}))?$/, ]; for (const pattern of patterns) { const match = str.match(pattern); if (match) { const groups = match.slice(1).map(Number); if (pattern.source.includes('YYYY-MM-DD') || pattern.source.includes('YYYY/MM/DD') || pattern.source.includes('YYYY.MM.DD')) { // YYYY-MM-DD 格式 const [year, month, day, hour = 0, minute = 0, second = 0] = groups; return new Date(year, month - 1, day, hour, minute, second); } else if (pattern.source.includes('DD-MM-YYYY') || pattern.source.includes('DD/MM/YYYY') || pattern.source.includes('DD.MM.YYYY')) { // DD-MM-YYYY 格式 const [day, month, year, hour = 0, minute = 0, second = 0] = groups; return new Date(year, month - 1, day, hour, minute, second); } else if (pattern.source.includes('MM-DD-YYYY') || pattern.source.includes('MM/DD/YYYY') || pattern.source.includes('MM.DD.YYYY')) { // MM-DD-YYYY 格式 const [month, day, year, hour = 0, minute = 0, second = 0] = groups; return new Date(year, month - 1, day, hour, minute, second); } else if (pattern.source.includes('YYYYMMDD')) { // YYYYMMDD 格式 const [year, month, day] = groups; return new Date(year, month - 1, day); } } } // 如果都不匹配,返回无效日期 return new Date(NaN); } /** * 比较两个日期值,支持多种常见格式 * @param a 第一个日期值 * @param b 第二个日期值 * @returns 比较结果:-1 表示 a < b,0 表示 a = b,1 表示 a > b */ function compareDates(a, b) { const aDate = parseDate(a); const bDate = parseDate(b); if (isNaN(aDate.getTime()) && isNaN(bDate.getTime())) { return 0; // 都是无效日期 } if (isNaN(aDate.getTime())) { return -1; // a 是无效日期,排在前面 } if (isNaN(bDate.getTime())) { return 1; // b 是无效日期,排在前面 } return aDate.getTime() - bDate.getTime(); } export { debounce, throttle, generateShortUUID, toLeaf, sortFixed, calCrossSpan, getMaxRow, decodeSpreadsheetStr, encodeToSpreadsheetStr, mergeRowCell, mergeColCell, getSpanArrByRow, getSpanObjByColumn, getCssVar, parseDate, compareDates, filterHiddenColumns, }; //# sourceMappingURL=util.js.map