UNPKG

vxe-table-ro-test

Version:

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

305 lines (304 loc) 16.9 kB
import { createCommentVNode, defineComponent, h, ref, inject, nextTick, watch, onMounted, onUnmounted } from 'vue'; import XEUtils from 'xe-utils'; import { VxeUI } from '../../ui'; import { convertHeaderColumnToRows, getColReMinWidth } from './util'; import { hasClass, getOffsetPos, addClass, removeClass } from '../../ui/src/dom'; const { renderer } = VxeUI; const renderType = 'header'; export default defineComponent({ name: 'VxeTableHeader', props: { tableData: Array, tableColumn: Array, tableGroupColumn: Array, fixedColumn: Array, fixedType: { type: String, default: null } }, setup(props) { const $xeTable = inject('$xeTable', {}); const { xID, props: tableProps, reactData: tableReactData, internalData: tableInternalData } = $xeTable; const { refElem: tableRefElem, refTableBody, refLeftContainer, refRightContainer, refCellResizeBar } = $xeTable.getRefMaps(); const { computeColumnOpts } = $xeTable.getComputeMaps(); const headerColumn = ref([]); const refElem = ref(); const refHeaderTable = ref(); const refHeaderColgroup = ref(); const refHeaderTHead = ref(); const refHeaderXSpace = ref(); const refHeaderBorderRepair = ref(); const uploadColumn = () => { const { isGroup } = tableReactData; headerColumn.value = isGroup ? convertHeaderColumnToRows(props.tableGroupColumn) : []; }; const resizeMousedown = (evnt, params) => { const { column } = params; const { fixedType } = props; const tableBody = refTableBody.value; const leftContainerElem = refLeftContainer.value; const rightContainerElem = refRightContainer.value; const resizeBarElem = refCellResizeBar.value; const { clientX: dragClientX } = evnt; const wrapperElem = refElem.value; const dragBtnElem = evnt.target; const cell = params.cell = dragBtnElem.parentNode; let dragLeft = 0; const tableBodyElem = tableBody.$el; const pos = getOffsetPos(dragBtnElem, wrapperElem); const dragBtnWidth = dragBtnElem.clientWidth; const dragBtnOffsetWidth = Math.floor(dragBtnWidth / 2); const minInterval = getColReMinWidth(params) - dragBtnOffsetWidth; // 列之间的最小间距 let dragMinLeft = pos.left - cell.clientWidth + dragBtnWidth + minInterval; let dragPosLeft = pos.left + dragBtnOffsetWidth; const domMousemove = document.onmousemove; const domMouseup = document.onmouseup; const isLeftFixed = fixedType === 'left'; const isRightFixed = fixedType === 'right'; const tableEl = tableRefElem.value; // 计算左右侧固定列偏移量 let fixedOffsetWidth = 0; if (isLeftFixed || isRightFixed) { const siblingProp = isLeftFixed ? 'nextElementSibling' : 'previousElementSibling'; let tempCellElem = cell[siblingProp]; while (tempCellElem) { if (hasClass(tempCellElem, 'fixed--hidden')) { break; } else if (!hasClass(tempCellElem, 'col--group')) { fixedOffsetWidth += tempCellElem.offsetWidth; } tempCellElem = tempCellElem[siblingProp]; } if (isRightFixed && rightContainerElem) { dragPosLeft = rightContainerElem.offsetLeft + fixedOffsetWidth; } } // 处理拖动事件 const updateEvent = function (evnt) { evnt.stopPropagation(); evnt.preventDefault(); const offsetX = evnt.clientX - dragClientX; let left = dragPosLeft + offsetX; const scrollLeft = fixedType ? 0 : tableBodyElem.scrollLeft; if (isLeftFixed) { // 左固定列(不允许超过右侧固定列、不允许超过右边距) left = Math.min(left, (rightContainerElem ? rightContainerElem.offsetLeft : tableBodyElem.clientWidth) - fixedOffsetWidth - minInterval); } else if (isRightFixed) { // 右侧固定列(不允许超过左侧固定列、不允许超过左边距) dragMinLeft = (leftContainerElem ? leftContainerElem.clientWidth : 0) + fixedOffsetWidth + minInterval; left = Math.min(left, dragPosLeft + cell.clientWidth - minInterval); } else { dragMinLeft = Math.max(tableBodyElem.scrollLeft, dragMinLeft); // left = Math.min(left, tableBodyElem.clientWidth + tableBodyElem.scrollLeft - 40) } dragLeft = Math.max(left, dragMinLeft); resizeBarElem.style.left = `${dragLeft - scrollLeft}px`; }; tableReactData._isResize = true; addClass(tableEl, 'drag--resize'); resizeBarElem.style.display = 'block'; document.onmousemove = updateEvent; document.onmouseup = function (evnt) { document.onmousemove = domMousemove; document.onmouseup = domMouseup; const resizeWidth = column.renderWidth + (isRightFixed ? dragPosLeft - dragLeft : dragLeft - dragPosLeft); column.resizeWidth = resizeWidth; resizeBarElem.style.display = 'none'; tableReactData._isResize = false; tableInternalData._lastResizeTime = Date.now(); $xeTable.analyColumnWidth(); $xeTable.recalculate(true).then(() => { $xeTable.saveCustomStore('update:visible'); $xeTable.updateCellAreas(); $xeTable.dispatchEvent('resizable-change', Object.assign(Object.assign({}, params), { resizeWidth }), evnt); setTimeout(() => $xeTable.recalculate(true), 300); }); removeClass(tableEl, 'drag--resize'); }; updateEvent(evnt); if ($xeTable.closeMenu) { $xeTable.closeMenu(); } }; watch(() => props.tableColumn, uploadColumn); onMounted(() => { nextTick(() => { const { fixedType } = props; const { internalData } = $xeTable; const { elemStore } = internalData; const prefix = `${fixedType || 'main'}-header-`; elemStore[`${prefix}wrapper`] = refElem; elemStore[`${prefix}table`] = refHeaderTable; elemStore[`${prefix}colgroup`] = refHeaderColgroup; elemStore[`${prefix}list`] = refHeaderTHead; elemStore[`${prefix}xSpace`] = refHeaderXSpace; elemStore[`${prefix}repair`] = refHeaderBorderRepair; uploadColumn(); }); }); onUnmounted(() => { const { fixedType } = props; const { internalData } = $xeTable; const { elemStore } = internalData; const prefix = `${fixedType || 'main'}-header-`; elemStore[`${prefix}wrapper`] = null; elemStore[`${prefix}table`] = null; elemStore[`${prefix}colgroup`] = null; elemStore[`${prefix}list`] = null; elemStore[`${prefix}xSpace`] = null; elemStore[`${prefix}repair`] = null; }); const renderVN = () => { const { fixedType, fixedColumn, tableColumn } = props; const { resizable, border, columnKey, headerRowClassName, headerCellClassName, headerRowStyle, headerCellStyle, showHeaderOverflow: allColumnHeaderOverflow, headerAlign: allHeaderAlign, align: allAlign, mouseConfig } = tableProps; const { isGroup, currentColumn, scrollXLoad, overflowX, scrollbarWidth } = tableReactData; const { visibleColumn } = tableInternalData; const columnOpts = computeColumnOpts.value; let headerGroups = headerColumn.value; let renderColumnList = tableColumn; if (isGroup) { renderColumnList = visibleColumn; } else { // 如果是使用优化模式 if (fixedType) { if (scrollXLoad || allColumnHeaderOverflow) { renderColumnList = fixedColumn; } } headerGroups = [renderColumnList]; } return h('div', { ref: refElem, class: ['vxe-table--header-wrapper', fixedType ? `fixed-${fixedType}--wrapper` : 'body--wrapper'], xid: xID }, [ fixedType ? createCommentVNode() : h('div', { ref: refHeaderXSpace, class: 'vxe-body--x-space' }), h('table', { ref: refHeaderTable, class: 'vxe-table--header', xid: xID, cellspacing: 0, cellpadding: 0, border: 0 }, [ /** * 列宽 */ h('colgroup', { ref: refHeaderColgroup }, renderColumnList.map((column, $columnIndex) => { return h('col', { name: column.id, key: $columnIndex }); }).concat(scrollbarWidth ? [ h('col', { name: 'col_gutter' }) ] : [])), /** * 头部 */ h('thead', { ref: refHeaderTHead }, headerGroups.map((cols, $rowIndex) => { return h('tr', { class: ['vxe-header--row', headerRowClassName ? (XEUtils.isFunction(headerRowClassName) ? headerRowClassName({ $table: $xeTable, $rowIndex, fixed: fixedType, type: renderType }) : headerRowClassName) : ''], style: headerRowStyle ? (XEUtils.isFunction(headerRowStyle) ? headerRowStyle({ $table: $xeTable, $rowIndex, fixed: fixedType, type: renderType }) : headerRowStyle) : null }, cols.map((column, $columnIndex) => { const { type, showHeaderOverflow, headerAlign, align, headerClassName, editRender, cellRender } = column; const colid = column.id; const renderOpts = editRender || cellRender; const compConf = renderOpts ? renderer.get(renderOpts.name) : null; const isColGroup = column.children && column.children.length; const fixedHiddenColumn = fixedType ? (column.fixed !== fixedType && !isColGroup) : !!column.fixed && overflowX; const headOverflow = XEUtils.eqNull(showHeaderOverflow) ? allColumnHeaderOverflow : showHeaderOverflow; const headAlign = headerAlign || (compConf ? compConf.tableHeaderCellAlign : '') || allHeaderAlign || align || (compConf ? compConf.tableCellAlign : '') || allAlign; let showEllipsis = headOverflow === 'ellipsis'; const showTitle = headOverflow === 'title'; const showTooltip = headOverflow === true || headOverflow === 'tooltip'; let hasEllipsis = showTitle || showTooltip || showEllipsis; const hasFilter = column.filters && column.filters.some((item) => item.checked); const columnIndex = $xeTable.getColumnIndex(column); const _columnIndex = $xeTable.getVTColumnIndex(column); const params = { $table: $xeTable, $grid: $xeTable.xegrid, $rowIndex, column, columnIndex, $columnIndex, _columnIndex, fixed: fixedType, type: renderType, isHidden: fixedHiddenColumn, hasFilter }; const thOns = { onClick: (evnt) => $xeTable.triggerHeaderCellClickEvent(evnt, params), onDblclick: (evnt) => $xeTable.triggerHeaderCellDblclickEvent(evnt, params) }; // 横向虚拟滚动不支持动态行高 if (scrollXLoad && !hasEllipsis) { showEllipsis = hasEllipsis = true; } // 按下事件处理 if (mouseConfig) { thOns.onMousedown = (evnt) => $xeTable.triggerHeaderCellMousedownEvent(evnt, params); } return h('th', Object.assign(Object.assign({ class: ['vxe-header--column', colid, { [`col--${headAlign}`]: headAlign, [`col--${type}`]: type, 'col--last': $columnIndex === cols.length - 1, 'col--fixed': column.fixed, 'col--group': isColGroup, 'col--ellipsis': hasEllipsis, 'fixed--hidden': fixedHiddenColumn, 'is--sortable': column.sortable, 'col--filter': !!column.filters, 'is--filter-active': hasFilter, 'col--current': currentColumn === column }, headerClassName ? (XEUtils.isFunction(headerClassName) ? headerClassName(params) : headerClassName) : '', headerCellClassName ? (XEUtils.isFunction(headerCellClassName) ? headerCellClassName(params) : headerCellClassName) : '' ], colid, colspan: column.colSpan > 1 ? column.colSpan : null, rowspan: column.rowSpan > 1 ? column.rowSpan : null, style: headerCellStyle ? (XEUtils.isFunction(headerCellStyle) ? headerCellStyle(params) : headerCellStyle) : null }, thOns), { key: columnKey || columnOpts.useKey || isColGroup ? colid : $columnIndex }), [ h('div', { class: ['vxe-cell', { 'c--title': showTitle, 'c--tooltip': showTooltip, 'c--ellipsis': showEllipsis }] }, column.renderHeader(params)), /** * 列宽拖动 */ !fixedHiddenColumn && !isColGroup && (XEUtils.isBoolean(column.resizable) ? column.resizable : (columnOpts.resizable || resizable)) ? h('div', { class: ['vxe-resizable', { 'is--line': !border || border === 'none' }], onMousedown: (evnt) => resizeMousedown(evnt, params) }) : null ]); }).concat(scrollbarWidth ? [ h('th', { class: 'vxe-header--gutter col--gutter' }) ] : [])); })) ]), /** * 其他 */ h('div', { ref: refHeaderBorderRepair, class: 'vxe-table--header-border-line' }) ]); }; return renderVN; } });