UNPKG

vxe-table-select-area

Version:

一个基于 vxe-table 的可区域选中复制、粘贴的组件

370 lines (366 loc) 14.4 kB
import XEUtils from 'xe-utils' import DomTools, { browse } from '../../tools/dom' function getTargetOffset (target, container) { let offsetTop = 0 let offsetLeft = 0 const triggerCheckboxLabel = !browse.firefox && DomTools.hasClass(target, 'vxe-checkbox--label') if (triggerCheckboxLabel) { const checkboxLabelStyle = getComputedStyle(target) offsetTop -= XEUtils.toNumber(checkboxLabelStyle.paddingTop) offsetLeft -= XEUtils.toNumber(checkboxLabelStyle.paddingLeft) } while (target && target !== container) { offsetTop += target.offsetTop offsetLeft += target.offsetLeft target = target.offsetParent if (triggerCheckboxLabel) { const checkboxStyle = getComputedStyle(target) offsetTop -= XEUtils.toNumber(checkboxStyle.paddingTop) offsetLeft -= XEUtils.toNumber(checkboxStyle.paddingLeft) } } return { offsetTop, offsetLeft } } function getCheckboxRangeRows (_vm, params, targetTrElem, moveRange) { let countHeight = 0 let rangeRows = [] const isDown = moveRange > 0 const moveSize = moveRange > 0 ? moveRange : (Math.abs(moveRange) + targetTrElem.offsetHeight) const { afterFullData, scrollYStore, scrollYLoad } = _vm if (scrollYLoad) { const _rowIndex = _vm.getVTRowIndex(params.row) if (isDown) { rangeRows = afterFullData.slice(_rowIndex, _rowIndex + Math.ceil(moveSize / scrollYStore.rowHeight)) } else { rangeRows = afterFullData.slice(_rowIndex - Math.floor(moveSize / scrollYStore.rowHeight) + 1, _rowIndex + 1) } } else { const siblingProp = isDown ? 'next' : 'previous' while (targetTrElem && countHeight < moveSize) { rangeRows.push(_vm.getRowNode(targetTrElem).item) countHeight += targetTrElem.offsetHeight targetTrElem = targetTrElem[`${siblingProp}ElementSibling`] } } return rangeRows } export default { methods: { // 处理 Tab 键移动 moveTabSelected (args, isLeft, evnt) { const { afterFullData, visibleColumn, editConfig, editOpts } = this let targetRow let targetRowIndex let targetColumnIndex const params = Object.assign({}, args) const _rowIndex = this.getVTRowIndex(params.row) const _columnIndex = this.getVTColumnIndex(params.column) evnt.preventDefault() if (isLeft) { // 向左 if (_columnIndex <= 0) { // 如果已经是第一列,则移动到上一行 if (_rowIndex > 0) { targetRowIndex = _rowIndex - 1 targetRow = afterFullData[targetRowIndex] targetColumnIndex = visibleColumn.length - 1 } } else { targetColumnIndex = _columnIndex - 1 } } else { if (_columnIndex >= visibleColumn.length - 1) { // 如果已经是第一列,则移动到上一行 if (_rowIndex < afterFullData.length - 1) { targetRowIndex = _rowIndex + 1 targetRow = afterFullData[targetRowIndex] targetColumnIndex = 0 } } else { targetColumnIndex = _columnIndex + 1 } } const targetColumn = visibleColumn[targetColumnIndex] if (targetColumn) { if (targetRow) { params.rowIndex = targetRowIndex params.row = targetRow } else { params.rowIndex = _rowIndex } params.columnIndex = targetColumnIndex params.column = targetColumn params.cell = this.getCell(params.row, params.column) if (editConfig) { if (editOpts.trigger === 'click' || editOpts.trigger === 'dblclick') { if (editOpts.mode === 'row') { this.handleActived(params, evnt) } else { this.scrollToRow(params.row, params.column) .then(() => this.handleSelected(params, evnt)) } } } else { this.scrollToRow(params.row, params.column) .then(() => this.handleSelected(params, evnt)) } } }, // 处理当前行方向键移动 moveCurrentRow (isUpArrow, isDwArrow, evnt) { const { currentRow, treeConfig, treeOpts, afterFullData } = this let targetRow evnt.preventDefault() if (currentRow) { if (treeConfig) { const { index, items } = XEUtils.findTree(afterFullData, item => item === currentRow, treeOpts) if (isUpArrow && index > 0) { targetRow = items[index - 1] } else if (isDwArrow && index < items.length - 1) { targetRow = items[index + 1] } } else { const _rowIndex = this.getVTRowIndex(currentRow) if (isUpArrow && _rowIndex > 0) { targetRow = afterFullData[_rowIndex - 1] } else if (isDwArrow && _rowIndex < afterFullData.length - 1) { targetRow = afterFullData[_rowIndex + 1] } } } else { targetRow = afterFullData[0] } if (targetRow) { const params = { $table: this, row: targetRow } this.scrollToRow(targetRow) .then(() => this.triggerCurrentRowEvent(evnt, params)) } }, // 处理可编辑方向键移动 moveSelected (args, isLeftArrow, isUpArrow, isRightArrow, isDwArrow, evnt) { const { afterFullData, visibleColumn } = this const params = Object.assign({}, args) const _rowIndex = this.getVTRowIndex(params.row) const _columnIndex = this.getVTColumnIndex(params.column) evnt.preventDefault() if (isUpArrow && _rowIndex > 0) { // 移动到上一行 params.rowIndex = _rowIndex - 1 params.row = afterFullData[params.rowIndex] } else if (isDwArrow && _rowIndex < afterFullData.length - 1) { // 移动到下一行 params.rowIndex = _rowIndex + 1 params.row = afterFullData[params.rowIndex] } else if (isLeftArrow && _columnIndex) { // 移动到左侧单元格 params.columnIndex = _columnIndex - 1 params.column = visibleColumn[params.columnIndex] } else if (isRightArrow && _columnIndex < visibleColumn.length - 1) { // 移动到右侧单元格 params.columnIndex = _columnIndex + 1 params.column = visibleColumn[params.columnIndex] } this.scrollToRow(params.row, params.column).then(() => { params.cell = this.getCell(params.row, params.column) this.handleSelected(params, evnt) }) }, /** * 表头单元格按下事件 */ triggerHeaderCellMousedownEvent (evnt, params) { const { mouseConfig, mouseOpts } = this if (mouseConfig && mouseOpts.area && this.handleHeaderCellAreaEvent) { const cell = evnt.currentTarget const triggerSort = DomTools.getEventTargetNode(evnt, cell, 'vxe-cell--sort').flag const triggerFilter = DomTools.getEventTargetNode(evnt, cell, 'vxe-cell--filter').flag this.handleHeaderCellAreaEvent(evnt, Object.assign({ cell, triggerSort, triggerFilter }, params)) } this.focus() this.closeMenu() }, /** * 单元格按下事件 */ triggerCellMousedownEvent (evnt, params) { const cell = evnt.currentTarget params.cell = cell this.handleCellMousedownEvent(evnt, params) this.focus() this.closeFilter() this.closeMenu() }, handleCellMousedownEvent (evnt, params) { const { editConfig, editOpts, handleSelected, checkboxConfig, checkboxOpts, mouseConfig, mouseOpts } = this /*** * 新增开始 */ if (mouseConfig && mouseOpts.area && this.handleCellAreaEvent) { this.handleCellAreaEvent(evnt, params) } if (checkboxConfig && checkboxOpts.range) { this.handleCheckboxRangeEvent(evnt, params) } if (mouseConfig && mouseOpts.selected) { if (!editConfig || editOpts.mode === 'cell') { handleSelected(params, evnt) } } // 新增结束 // if (mouseConfig && mouseOpts.area && this.handleCellAreaEvent) { // return this.handleCellAreaEvent(evnt, params) // } else { // if (checkboxConfig && checkboxOpts.range) { // this.handleCheckboxRangeEvent(evnt, params) // } // if (mouseConfig && mouseOpts.selected) { // if (!editConfig || editOpts.mode === 'cell') { // handleSelected(params, evnt) // } // } // } }, handleCheckboxRangeEvent (evnt, params) { const { column, cell } = params if (column.type === 'checkbox') { const { $el, elemStore } = this const disX = evnt.clientX const disY = evnt.clientY const bodyWrapperElem = elemStore[`${column.fixed || 'main'}-body-wrapper`] || elemStore['main-body-wrapper'] const checkboxRangeElem = bodyWrapperElem.querySelector('.vxe-table--checkbox-range') const domMousemove = document.onmousemove const domMouseup = document.onmouseup const trElem = cell.parentNode const selectRecords = this.getCheckboxRecords() let lastRangeRows = [] const marginSize = 1 const offsetRest = getTargetOffset(evnt.target, bodyWrapperElem) const startTop = offsetRest.offsetTop + evnt.offsetY const startLeft = offsetRest.offsetLeft + evnt.offsetX const startScrollTop = bodyWrapperElem.scrollTop const rowHeight = trElem.offsetHeight let mouseScrollTimeout = null let isMouseScrollDown = false let mouseScrollSpaceSize = 1 const triggerEvent = (type, evnt) => { this.emitEvent(`checkbox-range-${type}`, { records: this.getCheckboxRecords(), reserves: this.getCheckboxReserveRecords() }, evnt) } const handleChecked = (evnt) => { const { clientX, clientY } = evnt const offsetLeft = clientX - disX const offsetTop = clientY - disY + (bodyWrapperElem.scrollTop - startScrollTop) let rangeHeight = Math.abs(offsetTop) let rangeWidth = Math.abs(offsetLeft) let rangeTop = startTop let rangeLeft = startLeft if (offsetTop < marginSize) { // 向上 rangeTop += offsetTop if (rangeTop < marginSize) { rangeTop = marginSize rangeHeight = startTop } } else { // 向下 rangeHeight = Math.min(rangeHeight, bodyWrapperElem.scrollHeight - startTop - marginSize) } if (offsetLeft < marginSize) { // 向左 rangeLeft += offsetLeft if (rangeWidth > startLeft) { rangeLeft = marginSize rangeWidth = startLeft } } else { // 向右 rangeWidth = Math.min(rangeWidth, bodyWrapperElem.clientWidth - startLeft - marginSize) } checkboxRangeElem.style.height = `${rangeHeight}px` checkboxRangeElem.style.width = `${rangeWidth}px` checkboxRangeElem.style.left = `${rangeLeft}px` checkboxRangeElem.style.top = `${rangeTop}px` checkboxRangeElem.style.display = 'block' const rangeRows = getCheckboxRangeRows(this, params, trElem, offsetTop < marginSize ? -rangeHeight : rangeHeight) // 至少滑动 10px 才能有效匹配 if (rangeHeight > 10 && rangeRows.length !== lastRangeRows.length) { lastRangeRows = rangeRows if (evnt.ctrlKey) { rangeRows.forEach(row => { this.handleSelectRow({ row }, selectRecords.indexOf(row) === -1) }) } else { this.setAllCheckboxRow(false) this.handleCheckedCheckboxRow(rangeRows, true, false) } triggerEvent('change', evnt) } } // 停止鼠标滚动 const stopMouseScroll = () => { clearTimeout(mouseScrollTimeout) mouseScrollTimeout = null } // 开始鼠标滚动 const startMouseScroll = (evnt) => { stopMouseScroll() mouseScrollTimeout = setTimeout(() => { if (mouseScrollTimeout) { const { scrollLeft, scrollTop, clientHeight, scrollHeight } = bodyWrapperElem const topSize = Math.ceil(mouseScrollSpaceSize * 50 / rowHeight) if (isMouseScrollDown) { if (scrollTop + clientHeight < scrollHeight) { this.scrollTo(scrollLeft, scrollTop + topSize) startMouseScroll(evnt) handleChecked(evnt) } else { stopMouseScroll() } } else { if (scrollTop) { this.scrollTo(scrollLeft, scrollTop - topSize) startMouseScroll(evnt) handleChecked(evnt) } else { stopMouseScroll() } } } }, 50) } DomTools.addClass($el, 'drag--range') document.onmousemove = evnt => { evnt.preventDefault() evnt.stopPropagation() const { clientY } = evnt const { boundingTop } = DomTools.getAbsolutePos(bodyWrapperElem) // 如果超过可视区,触发滚动 if (clientY < boundingTop) { isMouseScrollDown = false mouseScrollSpaceSize = boundingTop - clientY if (!mouseScrollTimeout) { startMouseScroll(evnt) } } else if (clientY > boundingTop + bodyWrapperElem.clientHeight) { isMouseScrollDown = true mouseScrollSpaceSize = clientY - boundingTop - bodyWrapperElem.clientHeight if (!mouseScrollTimeout) { startMouseScroll(evnt) } } else if (mouseScrollTimeout) { stopMouseScroll() } handleChecked(evnt) } document.onmouseup = (evnt) => { stopMouseScroll() DomTools.removeClass($el, 'drag--range') checkboxRangeElem.removeAttribute('style') document.onmousemove = domMousemove document.onmouseup = domMouseup triggerEvent('end', evnt) } triggerEvent('start', evnt) } } } }