UNPKG

vxe-table-select-area

Version:

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

271 lines (269 loc) 11.1 kB
import XEUtils from 'xe-utils' import UtilTools, { isEnableConf } from '../../tools/utils' import DomTools from '../../tools/dom' import VXETable from '../../v-x-e-table' import { warnLog } from '../../tools/log' export default { methods: { /** * 关闭快捷菜单 */ _closeMenu () { Object.assign(this.ctxMenuStore, { visible: false, selected: null, selectChild: null, showChild: false }) return this.$nextTick() }, // 处理菜单的移动 moveCtxMenu (evnt, keyCode, ctxMenuStore, property, operKey, operRest, menuList) { let selectItem const selectIndex = XEUtils.findIndexOf(menuList, item => ctxMenuStore[property] === item) if (keyCode === operKey) { if (operRest && UtilTools.hasChildrenList(ctxMenuStore.selected)) { ctxMenuStore.showChild = true } else { ctxMenuStore.showChild = false ctxMenuStore.selectChild = null } } else if (keyCode === 38) { for (let len = selectIndex - 1; len >= 0; len--) { if (menuList[len].visible !== false) { selectItem = menuList[len] break } } ctxMenuStore[property] = selectItem || menuList[menuList.length - 1] } else if (keyCode === 40) { for (let index = selectIndex + 1; index < menuList.length; index++) { if (menuList[index].visible !== false) { selectItem = menuList[index] break } } ctxMenuStore[property] = selectItem || menuList[0] } else if (ctxMenuStore[property] && (keyCode === 13 || keyCode === 32)) { this.ctxMenuLinkEvent(evnt, ctxMenuStore[property]) } }, /** * 快捷菜单事件处理 */ handleGlobalContextmenuEvent (evnt) { const { $refs, tId, editStore, menuConfig, contextMenu, ctxMenuStore, ctxMenuOpts, mouseConfig, mouseOpts } = this const { selected } = editStore const layoutList = ['header', 'body', 'footer'] if (isEnableConf(menuConfig) || contextMenu) { if (ctxMenuStore.visible && $refs.ctxWrapper && DomTools.getEventTargetNode(evnt, $refs.ctxWrapper.$el).flag) { evnt.preventDefault() return } if (this._keyCtx) { const type = 'body' const params = { type, $grid: this.$xegrid, $table: this, keyboard: true, columns: this.visibleColumn.slice(0), $event: evnt } // 如果开启单元格区域 if (mouseConfig && mouseOpts.area) { const activeArea = this.getActiveCellArea() if (activeArea && activeArea.row && activeArea.column) { params.row = activeArea.row params.column = activeArea.column this.openContextMenu(evnt, type, params) return } } else if (mouseConfig && mouseOpts.selected) { // 如果启用键盘导航且已选中单元格 if (selected.row && selected.column) { params.row = selected.row params.column = selected.column this.openContextMenu(evnt, type, params) return } } } // 分别匹配表尾、内容、表尾的快捷菜单 for (let index = 0; index < layoutList.length; index++) { const layout = layoutList[index] const columnTargetNode = DomTools.getEventTargetNode(evnt, this.$el, `vxe-${layout}--column`, target => { // target=td|th,直接向上找 table 去匹配即可 return target.parentNode.parentNode.parentNode.getAttribute('xid') === tId }) const params = { type: layout, $grid: this.$xegrid, $table: this, columns: this.visibleColumn.slice(0), $event: evnt } if (columnTargetNode.flag) { const cell = columnTargetNode.targetElem const column = this.getColumnNode(cell).item let typePrefix = `${layout}-` Object.assign(params, { column, columnIndex: this.getColumnIndex(column), cell }) if (layout === 'body') { const row = this.getRowNode(cell.parentNode).item typePrefix = '' params.row = row params.rowIndex = this.getRowIndex(row) } this.openContextMenu(evnt, layout, params) // 在 v4 中废弃事件 cell-context-menu、header-cell-context-menu、footer-cell-context-menu if (this.$listeners[`${typePrefix}cell-context-menu`]) { if (process.env.VUE_APP_VXE_TABLE_ENV === 'development') { warnLog('vxe.error.delEvent', [`${typePrefix}cell-context-menu`, `${typePrefix}cell-menu`]) } this.emitEvent(`${typePrefix}cell-context-menu`, params, evnt) } else { this.emitEvent(`${typePrefix}cell-menu`, params, evnt) } return } else if (DomTools.getEventTargetNode(evnt, this.$el, `vxe-table--${layout}-wrapper`, target => target.getAttribute('xid') === tId).flag) { if (ctxMenuOpts.trigger === 'cell') { evnt.preventDefault() } else { this.openContextMenu(evnt, layout, params) } return } } } if ($refs.filterWrapper && !DomTools.getEventTargetNode(evnt, $refs.filterWrapper.$el).flag) { this.closeFilter() } this.closeMenu() }, /** * 显示快捷菜单 */ openContextMenu (evnt, type, params) { const { isCtxMenu, ctxMenuStore, ctxMenuOpts } = this const config = ctxMenuOpts[type] const visibleMethod = ctxMenuOpts.visibleMethod if (config) { const { options, disabled } = config if (disabled) { evnt.preventDefault() } else if (isCtxMenu && options && options.length) { params.options = options this.preventEvent(evnt, 'event.showMenu', params, () => { if (!visibleMethod || visibleMethod(params)) { evnt.preventDefault() this.updateZindex() const { scrollTop, scrollLeft, visibleHeight, visibleWidth } = DomTools.getDomNode() let top = evnt.clientY + scrollTop let left = evnt.clientX + scrollLeft const handleVisible = () => { Object.assign(ctxMenuStore, { args: params, visible: true, list: options, selected: null, selectChild: null, showChild: false, style: { zIndex: this.tZindex, top: `${top}px`, left: `${left}px` } }) this.$nextTick(() => { const ctxElem = this.$refs.ctxWrapper.$el const clientHeight = ctxElem.clientHeight const clientWidth = ctxElem.clientWidth const { boundingTop, boundingLeft } = DomTools.getAbsolutePos(ctxElem) const offsetTop = boundingTop + clientHeight - visibleHeight const offsetLeft = boundingLeft + clientWidth - visibleWidth if (offsetTop > -10) { ctxMenuStore.style.top = `${Math.max(scrollTop + 2, top - clientHeight - 2)}px` } if (offsetLeft > -10) { ctxMenuStore.style.left = `${Math.max(scrollLeft + 2, left - clientWidth - 2)}px` } }) } const { keyboard, row, column } = params if (keyboard && row && column) { this.scrollToRow(row, column).then(() => { const cell = this.getCell(row, column) const { boundingTop, boundingLeft } = DomTools.getAbsolutePos(cell) top = boundingTop + scrollTop + Math.floor(cell.offsetHeight / 2) left = boundingLeft + scrollLeft + Math.floor(cell.offsetWidth / 2) handleVisible() }) } else { handleVisible() } } else { this.closeMenu() } }) } } this.closeFilter() }, ctxMenuMouseoverEvent (evnt, item, child) { const menuElem = evnt.currentTarget const ctxMenuStore = this.ctxMenuStore evnt.preventDefault() evnt.stopPropagation() ctxMenuStore.selected = item ctxMenuStore.selectChild = child if (!child) { ctxMenuStore.showChild = UtilTools.hasChildrenList(item) if (ctxMenuStore.showChild) { this.$nextTick(() => { const childWrapperElem = menuElem.nextElementSibling if (childWrapperElem) { const { boundingTop, boundingLeft, visibleHeight, visibleWidth } = DomTools.getAbsolutePos(menuElem) const posTop = boundingTop + menuElem.offsetHeight const posLeft = boundingLeft + menuElem.offsetWidth let left = '' let right = '' // 是否超出右侧 if (posLeft + childWrapperElem.offsetWidth > visibleWidth - 10) { left = 'auto' right = `${menuElem.offsetWidth}px` } // 是否超出底部 let top = '' let bottom = '' if (posTop + childWrapperElem.offsetHeight > visibleHeight - 10) { top = 'auto' bottom = '0' } childWrapperElem.style.left = left childWrapperElem.style.right = right childWrapperElem.style.top = top childWrapperElem.style.bottom = bottom } }) } } }, ctxMenuMouseoutEvent (evnt, item) { const ctxMenuStore = this.ctxMenuStore if (!item.children) { ctxMenuStore.selected = null } ctxMenuStore.selectChild = null }, /** * 快捷菜单点击事件 */ ctxMenuLinkEvent (evnt, menu) { // 如果一级菜单有配置 code 则允许点击,否则不能点击 if (!menu.disabled && (menu.code || !menu.children || !menu.children.length)) { const ctxMenuMethod = VXETable.menus.get(menu.code) const params = Object.assign({ menu, $grid: this.$xegrid, $table: this, $event: evnt }, this.ctxMenuStore.args) if (ctxMenuMethod) { ctxMenuMethod.call(this, params, evnt) } // 在 v4 中废弃事件 context-menu-click if (this.$listeners['context-menu-click']) { if (process.env.VUE_APP_VXE_TABLE_ENV === 'development') { warnLog('vxe.error.delEvent', ['context-menu-click', 'menu-click']) } this.emitEvent('context-menu-click', params, evnt) } else { this.emitEvent('menu-click', params, evnt) } this.closeMenu() } } } }