UNPKG

vxe-table-ro-test

Version:

一个基于 vue 的 PC 端表格组件,支持增删改查、虚拟树、列拖拽,懒加载、快捷菜单、数据校验、树形结构、打印、导入导出、自定义模板、渲染器、JSON 配置式...

415 lines (414 loc) 16.5 kB
import { watch, reactive } from 'vue'; import XEUtils from 'xe-utils'; import { ColumnInfo } from './columnInfo'; import { isPx, isScale } from '../../ui/src/dom'; const getAllConvertColumns = (columns, parentColumn) => { const result = []; columns.forEach((column) => { column.parentId = parentColumn ? parentColumn.id : null; if (column.visible) { if (column.children && column.children.length && column.children.some((column) => column.visible)) { result.push(column); result.push(...getAllConvertColumns(column.children, column)); } else { result.push(column); } } }); return result; }; export const convertHeaderColumnToRows = (originColumns) => { let maxLevel = 1; const traverse = (column, parent) => { if (parent) { column.level = parent.level + 1; if (maxLevel < column.level) { maxLevel = column.level; } } if (column.children && column.children.length && column.children.some((column) => column.visible)) { let colSpan = 0; column.children.forEach((subColumn) => { if (subColumn.visible) { traverse(subColumn, column); colSpan += subColumn.colSpan; } }); column.colSpan = colSpan; } else { column.colSpan = 1; } }; originColumns.forEach((column) => { column.level = 1; traverse(column); }); const rows = []; for (let i = 0; i < maxLevel; i++) { rows.push([]); } const allColumns = getAllConvertColumns(originColumns); allColumns.forEach((column) => { if (column.children && column.children.length && column.children.some((column) => column.visible)) { column.rowSpan = 1; } else { column.rowSpan = maxLevel - column.level + 1; } rows[column.level - 1].push(column); }); return rows; }; export function restoreScrollLocation($xeTable, scrollLeft, scrollTop) { const { internalData } = $xeTable; return $xeTable.clearScroll().then(() => { if (scrollLeft || scrollTop) { // 重置最后滚动状态 internalData.lastScrollLeft = 0; internalData.lastScrollTop = 0; // 还原滚动状态 return $xeTable.scrollTo(scrollLeft, scrollTop); } }); } export function removeScrollListener(scrollElem) { if (scrollElem && scrollElem._onscroll) { scrollElem.onscroll = null; } } export function restoreScrollListener(scrollElem) { if (scrollElem && scrollElem._onscroll) { scrollElem.onscroll = scrollElem._onscroll; } } /** * 生成行的唯一主键 */ export function getRowUniqueId() { return XEUtils.uniqueId('row_'); } // 行主键 key export function getRowkey($xeTable) { const { props } = $xeTable; const { computeRowOpts } = $xeTable.getComputeMaps(); const { rowId } = props; const rowOpts = computeRowOpts.value; return rowId || rowOpts.keyField || '_X_ROW_KEY'; } // 行主键 value export function getRowid($xeTable, row) { const rowid = XEUtils.get(row, getRowkey($xeTable)); return XEUtils.eqNull(rowid) ? '' : encodeURIComponent(rowid); } export const handleFieldOrColumn = ($xeTable, fieldOrColumn) => { if (fieldOrColumn) { return XEUtils.isString(fieldOrColumn) ? $xeTable.getColumnByField(fieldOrColumn) : fieldOrColumn; } return null; }; function getPaddingLeftRightSize(elem) { if (elem) { const computedStyle = getComputedStyle(elem); const paddingLeft = XEUtils.toNumber(computedStyle.paddingLeft); const paddingRight = XEUtils.toNumber(computedStyle.paddingRight); return paddingLeft + paddingRight; } return 0; } function getElemenMarginWidth(elem) { if (elem) { const computedStyle = getComputedStyle(elem); const marginLeft = XEUtils.toNumber(computedStyle.marginLeft); const marginRight = XEUtils.toNumber(computedStyle.marginRight); return elem.offsetWidth + marginLeft + marginRight; } return 0; } function queryCellElement(cell, selector) { return cell.querySelector('.vxe-cell' + selector); } export function toFilters(filters) { if (filters && XEUtils.isArray(filters)) { return filters.map(({ label, value, data, resetValue, checked }) => { return { label, value, data, resetValue, checked: !!checked, _checked: !!checked }; }); } return filters; } export function toTreePathSeq(path) { return path.map((num, i) => i % 2 === 0 ? (Number(num) + 1) : '.').join(''); } export function getCellValue(row, column) { return XEUtils.get(row, column.field); } export function setCellValue(row, column, value) { return XEUtils.set(row, column.field, value); } /** * 列宽拖动最大宽度 * @param params * @returns */ export function getColReMaxWidth(params) { const { $table } = params; const { computeResizableOpts } = $table.getComputeMaps(); const resizableOpts = computeResizableOpts.value; const { maxWidth: reMaxWidth } = resizableOpts; // 如果自定义调整宽度逻辑 if (reMaxWidth) { const customMaxWidth = XEUtils.isFunction(reMaxWidth) ? reMaxWidth(params) : reMaxWidth; if (customMaxWidth !== 'auto') { return Math.max(1, XEUtils.toNumber(customMaxWidth)); } } return -1; } /** * 列宽拖动最小宽度 * @param params * @returns */ export function getColReMinWidth(params) { const { $table, column, cell } = params; const { props: tableProps } = $table; const { computeResizableOpts } = $table.getComputeMaps(); const resizableOpts = computeResizableOpts.value; const { minWidth: reMinWidth } = resizableOpts; // 如果自定义调整宽度逻辑 if (reMinWidth) { const customMinWidth = XEUtils.isFunction(reMinWidth) ? reMinWidth(params) : reMinWidth; if (customMinWidth !== 'auto') { return Math.max(1, XEUtils.toNumber(customMinWidth)); } } const { showHeaderOverflow: allColumnHeaderOverflow } = tableProps; const { showHeaderOverflow, minWidth: colMinWidth } = column; const headOverflow = XEUtils.isUndefined(showHeaderOverflow) || XEUtils.isNull(showHeaderOverflow) ? allColumnHeaderOverflow : showHeaderOverflow; const showEllipsis = headOverflow === 'ellipsis'; const showTitle = headOverflow === 'title'; const showTooltip = headOverflow === true || headOverflow === 'tooltip'; const hasEllipsis = showTitle || showTooltip || showEllipsis; const minTitleWidth = XEUtils.floor((XEUtils.toNumber(getComputedStyle(cell).fontSize) || 14) * 1.6); const paddingLeftRight = getPaddingLeftRightSize(cell) + getPaddingLeftRightSize(queryCellElement(cell, '')); let mWidth = minTitleWidth + paddingLeftRight; // 默认最小宽处理 if (hasEllipsis) { const checkboxIconWidth = getPaddingLeftRightSize(queryCellElement(cell, '--title>.vxe-cell--checkbox')); const requiredIconWidth = getElemenMarginWidth(queryCellElement(cell, '>.vxe-cell--required-icon')); const editIconWidth = getElemenMarginWidth(queryCellElement(cell, '>.vxe-cell--edit-icon')); const prefixIconWidth = getElemenMarginWidth(queryCellElement(cell, '>.vxe-cell-title-prefix-icon')); const suffixIconWidth = getElemenMarginWidth(queryCellElement(cell, '>.vxe-cell-title-suffix-icon')); const sortIconWidth = getElemenMarginWidth(queryCellElement(cell, '>.vxe-cell--sort')); const filterIconWidth = getElemenMarginWidth(queryCellElement(cell, '>.vxe-cell--filter')); mWidth += checkboxIconWidth + requiredIconWidth + editIconWidth + prefixIconWidth + suffixIconWidth + filterIconWidth + sortIconWidth; } // 如果设置最小宽 if (colMinWidth) { const { refTableBody } = $table.getRefMaps(); const tableBody = refTableBody.value; const bodyElem = tableBody ? tableBody.$el : null; if (bodyElem) { if (isScale(colMinWidth)) { const bodyWidth = bodyElem.clientWidth - 1; const meanWidth = bodyWidth / 100; return Math.max(mWidth, Math.floor(XEUtils.toInteger(colMinWidth) * meanWidth)); } else if (isPx(colMinWidth)) { return Math.max(mWidth, XEUtils.toInteger(colMinWidth)); } } } return mWidth; } export function isColumnInfo(column) { return column && (column.constructor === ColumnInfo || column instanceof ColumnInfo); } export function createColumn($xeTable, options, renderOptions) { return isColumnInfo(options) ? options : reactive(new ColumnInfo($xeTable, options, renderOptions)); } export function watchColumn($xeTable, props, column) { Object.keys(props).forEach(name => { watch(() => props[name], (value) => { column.update(name, value); if ($xeTable) { if (name === 'filters') { $xeTable.setFilter(column, value); $xeTable.handleUpdateDataQueue(); } else if (['visible', 'fixed', 'width', 'minWidth', 'maxWidth'].includes(name)) { $xeTable.handleRefreshColumnQueue(); } } }); }); } export function assembleColumn($xeTable, elem, column, colgroup) { const { reactData } = $xeTable; const { staticColumns } = reactData; const parentElem = elem.parentNode; const parentColumn = colgroup ? colgroup.columnConfig : null; const parentCols = parentColumn ? parentColumn.children : staticColumns; if (parentElem && parentCols) { parentCols.splice(XEUtils.arrayIndexOf(parentElem.children, elem), 0, column); reactData.staticColumns = staticColumns.slice(0); } } export function destroyColumn($xeTable, column) { const { reactData } = $xeTable; const { staticColumns } = reactData; const matchObj = XEUtils.findTree(staticColumns, item => item.id === column.id, { children: 'children' }); if (matchObj) { matchObj.items.splice(matchObj.index, 1); } reactData.staticColumns = staticColumns.slice(0); } export function getRootColumn($xeTable, column) { const { internalData } = $xeTable; const { fullColumnIdData } = internalData; if (!column) { return null; } let parentColId = column.parentId; while (fullColumnIdData[parentColId]) { const column = fullColumnIdData[parentColId].column; parentColId = column.parentId; if (!parentColId) { return column; } } return column; } export function mergeBodyMethod(mergeList, _rowIndex, _columnIndex) { for (let mIndex = 0; mIndex < mergeList.length; mIndex++) { const { row: mergeRowIndex, col: mergeColIndex, rowspan: mergeRowspan, colspan: mergeColspan } = mergeList[mIndex]; if (mergeColIndex > -1 && mergeRowIndex > -1 && mergeRowspan && mergeColspan) { if (mergeRowIndex === _rowIndex && mergeColIndex === _columnIndex) { return { rowspan: mergeRowspan, colspan: mergeColspan }; } if (_rowIndex >= mergeRowIndex && _rowIndex < mergeRowIndex + mergeRowspan && _columnIndex >= mergeColIndex && _columnIndex < mergeColIndex + mergeColspan) { return { rowspan: 0, colspan: 0 }; } } } } export function clearTableDefaultStatus($xeTable) { const { props, internalData } = $xeTable; internalData.initStatus = false; $xeTable.clearSort(); $xeTable.clearCurrentRow(); $xeTable.clearCurrentColumn(); $xeTable.clearRadioRow(); $xeTable.clearRadioReserve(); $xeTable.clearCheckboxRow(); $xeTable.clearCheckboxReserve(); $xeTable.clearRowExpand(); $xeTable.clearTreeExpand(); $xeTable.clearTreeExpandReserve(); $xeTable.clearPendingRow(); if ($xeTable.clearFilter) { $xeTable.clearFilter(); } if ($xeTable.clearSelected && (props.keyboardConfig || props.mouseConfig)) { $xeTable.clearSelected(); } if ($xeTable.clearCellAreas && props.mouseConfig) { $xeTable.clearCellAreas(); $xeTable.clearCopyCellArea(); } return $xeTable.clearScroll(); } export function clearTableAllStatus($xeTable) { if ($xeTable.clearFilter) { $xeTable.clearFilter(); } return clearTableDefaultStatus($xeTable); } export function rowToVisible($xeTable, row) { const { reactData, internalData } = $xeTable; const { refTableBody } = $xeTable.getRefMaps(); const { scrollYLoad } = reactData; const { afterFullData, scrollYStore } = internalData; const tableBody = refTableBody.value; const bodyElem = tableBody ? tableBody.$el : null; if (bodyElem) { const trElem = bodyElem.querySelector(`[rowid="${getRowid($xeTable, row)}"]`); if (trElem) { const bodyHeight = bodyElem.clientHeight; const bodySrcollTop = bodyElem.scrollTop; const trOffsetParent = trElem.offsetParent; const trOffsetTop = trElem.offsetTop + (trOffsetParent ? trOffsetParent.offsetTop : 0); const trHeight = trElem.clientHeight; // 检测行是否在可视区中 if (trOffsetTop < bodySrcollTop || trOffsetTop > bodySrcollTop + bodyHeight) { // 向上定位 return $xeTable.scrollTo(null, trOffsetTop); } else if (trOffsetTop + trHeight >= bodyHeight + bodySrcollTop) { // 向下定位 return $xeTable.scrollTo(null, bodySrcollTop + trHeight); } } else { // 如果是虚拟渲染跨行滚动 if (scrollYLoad) { return $xeTable.scrollTo(null, ($xeTable.findRowIndexOf(afterFullData, row) - 1) * scrollYStore.rowHeight); } } } return Promise.resolve(); } export function colToVisible($xeTable, column) { const { reactData, internalData } = $xeTable; const { refTableBody } = $xeTable.getRefMaps(); const { columnStore, scrollXLoad } = reactData; const { visibleColumn } = internalData; const { leftList, rightList } = columnStore; const tableBody = refTableBody.value; const bodyElem = tableBody ? tableBody.$el : null; let offsetFixedLeft = 0; leftList.forEach(item => { offsetFixedLeft += item.renderWidth; }); let offsetFixedRight = 0; rightList.forEach(item => { offsetFixedRight += item.renderWidth; }); if (bodyElem) { const bodyWidth = bodyElem.clientWidth; const bodySrcollLeft = bodyElem.scrollLeft; const tdElem = bodyElem.querySelector(`.${column.id}`); if (tdElem) { const tdOffsetParent = tdElem.offsetParent; const tdOffsetLeft = tdElem.offsetLeft + (tdOffsetParent ? tdOffsetParent.offsetLeft : 0); const cellWidth = tdElem.clientWidth; // 检测是否在可视区中 if (tdOffsetLeft < (bodySrcollLeft + offsetFixedLeft)) { return $xeTable.scrollTo(tdOffsetLeft - offsetFixedLeft - 1); } else if ((tdOffsetLeft + cellWidth - bodySrcollLeft) > (bodyWidth - offsetFixedRight)) { return $xeTable.scrollTo((tdOffsetLeft + cellWidth) - (bodyWidth - offsetFixedRight - 1)); } } else { // 检测是否在虚拟渲染可视区中 if (scrollXLoad) { let scrollLeft = 0; const cellWidth = column.renderWidth; for (let index = 0; index < visibleColumn.length; index++) { const currCol = visibleColumn[index]; if (currCol === column || currCol.id === column.id) { break; } scrollLeft += currCol.renderWidth; } if (scrollLeft < bodySrcollLeft) { return $xeTable.scrollTo(scrollLeft - offsetFixedLeft - 1); } return $xeTable.scrollTo((scrollLeft + cellWidth) - (bodyWidth - offsetFixedRight - 1)); } } } return Promise.resolve(); }