import { onMounted, onBeforeUnmount, computed, watch, nextTick } from 'vue'; import type { Options, Column } from './type'; import State from './state'; import { delegateEvent, throttle } from "./util" export default class extends State { constructor(options: Options) { super(options); this._anyWhereSelectedCallbackSelected = this.anyWhereSelectedCallbackSelected.bind(this) const keypressUpDownLeftRight = (e) => this.keypressUpDownLeftRight(e) const keypressCtrlA = () => this.keypressCtrlA(); onMounted(() => { document.addEventListener('keydown', keypressCtrlA); document.addEventListener('keydown', keypressUpDownLeftRight); // 还未调试过 以下代码 // if(typeof this.props.customRow?.().onSelect === 'function'){ // this.props.customRow.onSelect?.(this.selectedRangeRowsData.value, this.selectedRangeRowsindex.value) // } }); onBeforeUnmount(() => { document.removeEventListener('keydown', keypressCtrlA); document.removeEventListener('keydown', keypressUpDownLeftRight); }); // 受控的行选择 if(this.props.customEditer?.selectedRowKeys){ watch(() => this.props.customEditer?.selectedRowKeys, (keys?: string[]) => { this.resetSelectedState(); const rowindexs = (this.dataSource.value?.map((row: any, rowindex) => { const cheosed = keys?.includes?.(row[this.props.rowKey]) if (cheosed) return rowindex; return undefined })?.filter(v => v !== undefined) ?? []) as number[]; this.selectedState.rows = rowindexs.length ? rowindexs : undefined; this.callbackSelected() }, { immediate: true, deep: true }) } } throttleIntoViewFirstSelected = throttle(this.intoViewFirstSelected, 100) selectAreasData = computed(() => { return { rowindexs: this.getSelectRowindexs(), columns: this.getSelectColumns(), records: this.getSelectRows(), source: this.dataSource, } }); /* // 内部选择行索引 selectedRangeRowsindex = computed(() => { let indexs = [] if(this.selectedState.startY !== undefined){ let startY = this.getMinStartY() const endY = this.getMaxEndY() while(startY <= endY){ indexs.push(startY) startY++ } } if(this.selectedState.rows){ indexs = this.selectedState.rows } return indexs }) // 内部选择行数据 selectedRangeRowsData = computed(() => { if(this.selectedState.startY !== undefined){ return this.dataSource.value.slice(this.getMinStartY(), this.getMaxEndY() + 1) } if(this.selectedState.rows){ return this.selectedState.rows.map((rowindex: number) => this.dataSource.value[rowindex]) } return [] }) */ // 是否选择整行 isSelectRow(rowindex: number) { return ( this.selectedState.startX === -1 && rowindex >= this.getMinStartY() && rowindex <= this.getMaxEndY() ) || this.selectedState.rows?.includes(rowindex); } // 是否选择表头 isSelectCol(rowindex: number, colindex: number, colspan: number = 0){ if (rowindex >= 0) return false; const index = (colindex + (colspan > 1 ? colspan - 1 : 0)) return ( this.selectedState.selectedTh && index >= this.getMinStartX() && index <= this.getMaxEndX() ) || !!this.selectedState.cells?.find((item) => { return item[0] === rowindex && item[1] === colindex; }); } // 行中是否有选中单元格 isSelectedTr(rowindex: number) { return ( this.selectedState.startY !== undefined && rowindex >= this.getMinStartY() && rowindex <= this.getMaxEndY() ); } // 是否选中了单元格 isSelectedTd(rowindex: number, colindex: number) { return ( this.selectedState.startX !== undefined && rowindex >= this.getMinStartY() && rowindex <= this.getMaxEndY() && colindex >= this.getMinStartX() && colindex <= this.getMaxEndX() ) || this.selectedState.cells?.find((item) => { return item[0] === rowindex && item[1] === colindex; }); } isFirstSelectedTh(colindex: number) { return this.isFirstSelectedTd(colindex); // return ( // this.isSelectCol(colindex) && this.selectedState.startX !== undefined && colindex === this.getMinStartX() // ); } isLastSelectedTh(colindex: number, colspan: number = 0) { return this.isLastSelectedTd(colindex, colspan); // if (this.selectedState.startX === undefined || !this.isSelectCol(colindex)) return false // if (colspan && colspan > 1) { // return colindex + Number(colspan) - 1 === this.getMaxEndX() // } // return colindex === this.getMaxEndX(); } isFirstSelectedTd(colindex: number) { return ( this.selectedState.startX !== undefined && colindex === this.getMinStartX() ); } isLastSelectedTd(colindex: number, colspan: number = 0) { if (this.selectedState.startX === undefined) return false if (colspan && colspan > 1) { return colindex + Number(colspan) - 1 === this.getMaxEndX() } return colindex === this.getMaxEndX(); } isLastSelectedTrTd(rowindex: number, rowspan: number = 0) { if (this.selectedState.startX === undefined || !rowspan || rowspan < 2) return false; return rowindex + Number(rowspan) - 1 === this.getMaxEndY() } isFirstSelectedTr(rowindex: number) { return ( this.selectedState.startY !== undefined && rowindex === this.getMinStartY() ); } isLastSelectedTr(rowindex: number) { return ( this.selectedState.startY !== undefined && rowindex === this.getMaxEndY() ); } // 框选单元格 selectMousedownHandel(rowindex: number, colindex: number, rowspan?: number) { if (colindex === undefined || rowindex === undefined) { // this.resetSelectedState(); return; }; if(this.props.customEditer?.disabledSelectCell) { this.selectRowsMousedown(rowindex) return; } if (event.button === 2) return; // 按住shift、选择其他单元格 if (event.shiftKey){ if (this.props.customEditer?.singleSelectCell) return; this.selectedState.endX = colindex; this.selectedState.endY = rowindex; this.callbackSelected() } // 按住ctrl、选择其他单元格 else if (event.ctrlKey || event.metaKey) { if (this.props.customEditer?.singleSelectCell) return; this.transRangeToRows() this.anyWhereSelectedCells(rowindex, colindex) } else { this.resetSelectedState(); this.selectedState.endX = this.selectedState.startX = colindex; this.selectedState.endY = this.selectedState.startY = rowindex; // 跨行格 if (rowspan && rowspan > 1){ this.selectedState.endY += rowspan - 1; } if (!this.props.customEditer?.singleSelectCell) { this.selectMmousemove('td', this.props.customEditer?.singleSelectRow ? this.getCellElement({ rowindex }) : undefined) } else { this.callbackSelected() } } this.listenResetSelectedState() } // 框选右下角的拖动点 dragContruneHandel() { // 存在自由点选时, 不允许拖动点 if(this.selectedState.cells || this.selectedState.rows){ return; } this.selectMmousemove('td') this.listenResetSelectedState() } // 框选整行 selectRowsMousedown(rowindex: number) { if (this.props.customEditer?.disabledSelectRow) return; if (event.button === 2) return; if (this.props.maxRowSelection === 0) return; // 按住shift、选择其他单元格 if (event.shiftKey){ if (this.props.customEditer?.singleSelectRow) return; if (this.selectedState.rows){ this.selectedState.startY = this.selectAreasData.value.rowindexs[0]; this.selectedState.rows = undefined; } this.selectedState.endY = rowindex; this.callbackSelected() } // 按住ctrl、选择其他行 else if (event.ctrlKey || event.metaKey) { if (this.props.customEditer?.singleSelectRow) return; this.anyWhereSelectedRows(rowindex) } else { this.selectedState.rows = undefined this.selectedState.cells = undefined this.selectedState.startY = this.selectedState.endY = rowindex; this.selectedState.startX = this.selectedState.endX = -1; this.selectedState.selectedTh = false if (event?.target.tagName !== 'TD' && !this.props.customEditer?.disabledSelectCell) return; if (!this.props.customEditer?.singleSelectRow) { this.selectMmousemove('tr') } else { this.callbackSelected() } } this.listenResetSelectedState() } // 框选表头 selectColumnsMousedown(rowindex: number, colindex: number, colspan: number = 0) { if (this.props.customEditer?.disabledSelectTh) return; if (event.button === 2) return; // shift if (event.shiftKey) { if (this.props.customEditer?.singleSelectCell) return; if (this.selectedState.selectedTh) { this.selectedState.endX = colindex + (colspan > 1 ? colspan - 1 : 0); } } // ctrl else if (event.ctrlKey || event.metaKey){ if (this.props.customEditer?.singleSelectCell) return; this.selectedState.selectedTh = false this.anyWhereSelectedCells(rowindex, colindex) return; } else { this.selectedState.rows = undefined this.selectedState.cells = undefined this.selectedState.selectedTh = true this.selectedState.startY = rowindex this.selectedState.endY = this.dataSource.value.length - 1 this.selectedState.startX = colindex; this.selectedState.endX = colindex + (colspan > 1 ? colspan - 1 : 0); const summaryTr = Array.from(this.table.value?.querySelectorAll('tfoot>tr[data-rowindex]')); if (summaryTr?.length){ this.selectedState.endY = Number(summaryTr.at(-1)?.getAttribute('data-rowindex')); } if (!this.props.customEditer?.singleSelectCell) { if(!this.props.showColDrag){ this.selectMmousemove('th,td', this.props.customEditer?.singleSelectRow ? this.getCellElement({ rowindex: rowindex }) : undefined) } } } this.listenResetSelectedState() } // 自由点选单元格 private anyWhereSelectedCells(rowindex: number, colindex: number) { this.transRangeCells() const tryfindIndex = this.selectedState.cells?.findIndex(item => item[0] === rowindex && item[1] === colindex) ?? -1; if (tryfindIndex > -1) { this.selectedState.cells?.splice(tryfindIndex, 1) if(this.selectedState.cells?.length === 0) this.selectedState.cells = undefined } else { this.selectedState.cells?.push([rowindex, colindex]) } document.body.removeEventListener('keyup', this._anyWhereSelectedCallbackSelected) document.body.addEventListener('keyup', this._anyWhereSelectedCallbackSelected) } // 范围转换-单元格 private transRangeCells() { if (this.selectedState.cells) return; this.selectedState.cells = [] // 此前选中过单元格 if (this.selectedState.startX !== undefined){ let startY = this.getMinStartY(); const endY = this.getMaxEndY(); let startX = this.getMinStartX(); const endX = this.getMaxEndX(); while(startY <= endY){ startX = this.getMinStartX(); while(startX <= endX){ this.selectedState.cells.push([ startY, startX ]) startX++; } startY++; } this.selectedState.endX = this.selectedState.startX = undefined; this.selectedState.endY = this.selectedState.startY = undefined; } // 选中过行 if (this.selectedState.startX === -1) { this.transRangeToRows() } } // 自由点选行 anyWhereSelectedRows(rowindex: number) { if ( this.props.maxRowSelection && this.props.maxRowSelection <= this.getSelectRowindexs().length ) { return; } this.transRangeToRows() const tryfindIndex = this.selectedState.rows?.findIndex(item => item === rowindex) ?? -1; if (tryfindIndex > -1) { this.selectedState.rows?.splice(tryfindIndex, 1) if(this.selectedState.rows?.length === 0) this.selectedState.rows = undefined } else { this.selectedState.rows?.push(rowindex) } document.body.removeEventListener('keyup', this._anyWhereSelectedCallbackSelected) document.body.addEventListener('keyup', this._anyWhereSelectedCallbackSelected) } // 范围转换-行 private transRangeToRows() { if (this.selectedState.rows) return; this.selectedState.rows = [] // 选择了范围 if(this.selectedState.startX === -1){ let startY = this.getMinStartY(); const endY = this.getMaxEndY(); while(startY <= endY){ this.selectedState.rows.push(startY) startY++; } this.selectedState.endX = this.selectedState.startX = undefined; this.selectedState.endY = this.selectedState.startY = undefined; } // 选中过单元格 if (this.selectedState.startX !== undefined) { this.transRangeCells() } } // 框选移动事件监听 private selectMmousemove(target: string, root?: HTMLElement | null) { const mousemoveHandel = async (currentTarget: HTMLElement, event: MouseEvent) => { const colindex = currentTarget?.getAttribute('data-colindex'); // const colspan = currentTarget?.getAttribute('colspan') || 0; const rowindex = currentTarget?.getAttribute('data-rowindex'); // const rowspan = currentTarget?.getAttribute('rowspan') || 0; if ( ![null, undefined].includes(colindex) && ( !this.props.maxColSelection || this.props.maxColSelection > this.getSelectColumns().length ) ) { const oldEndX = this.selectedState.endX this.selectedState.endX = Number(colindex) // + (colspan > 1 ? colspan - 1 : 0); if ( ![undefined , null].includes(this.props.maxColSelection) && this.props.maxColSelection < this.getSelectColumns().length ) { this.selectedState.endX = oldEndX } } if ( ![null, undefined].includes(rowindex) ) { const oldEndY = this.selectedState.endY this.selectedState.endY = Number(rowindex) // + (rowspan > 1 ? rowspan - 1 : 0); if ( ![undefined , null].includes(this.props.maxRowSelection) && this.props.maxRowSelection < this.getSelectRowindexs().length ) { this.selectedState.endY = oldEndY } } }; const cancel = delegateEvent(root || this.table?.value!, target, 'mouseenter', mousemoveHandel, true); const remove = () => { cancel() window.removeEventListener('mouseup', remove); this.callbackSelected(); } window.addEventListener('mouseup', remove); } private keypressUpDownLeftRight(e: any) { if (!this.currentView) return; if (this.props.customEditer?.disabledSelectRow || this.props.customEditer?.disabledSelectCell) return; if (![37, 38, 39, 40, 13, 9].includes(e.keyCode)) return false; if ( !this.selectedState.rows && !this.selectedState.cells && this.selectedState.startY === undefined ) { this.selectedState.startY = this.selectedState.endY = 0; this.selectedState.startX = this.selectedState.endX = -1; return false; } if(this.editState.status === 'doing' && ![undefined, null].includes(this.oldValue) && [37, 39, 9].includes(e.keyCode) ){ return false; } // 上 if (e.keyCode === 38) { if (this.selectedState.rows) tranRowsToRange('min') if (this.selectedState.cells) tranCellsToRange('min') this.selectedState.endY = Math.max(this.selectedState.endY - 1, 0); this.selectedState.startY = Math.max(this.selectedState.startY - 1, 0); } // 下 40 else if (e.keyCode === 40 || e.keyCode === 13) { if (this.selectedState.rows) tranRowsToRange('max') if (this.selectedState.cells) tranCellsToRange('max') const endY = Math.min(this.selectedState.endY + 1, this.dataSource?.value.length - 1); const startY = Math.min(this.selectedState.startY + 1, this.dataSource?.value.length - 1); this.selectedState.endY = endY this.selectedState.startY = startY if (startY === endY) { // @ts-ignore this.checkRowindexSetDefaultRowData?.(startY) } } // 左 37 else if (e.keyCode === 37) { if (this.selectedState.cells) tranCellsToRange('min') this.selectedState.endX = Math.max(this.selectedState.endX - 1, 0); this.selectedState.startX = Math.max(this.selectedState.startX - 1, 0); } // 右 39 else if (e.keyCode === 39 || e.keyCode === 9) { if (this.selectedState.cells) tranCellsToRange('max') this.selectedState.endX = Math.min(this.selectedState.endX + 1, this.tileColumns?.value?.length - 1); this.selectedState.startX = Math.min(this.selectedState.startX + 1, this.tileColumns?.value?.length - 1); } // 编辑状态中移动单元格 if (this.editState.status !== undefined) { Promise.resolve().then(() => { this.clickEditHandel?.(this.selectedState.startY, this.selectedState.startX) }) } const self = this function tranCellsToRange(type: 'min' | 'max') { if (!self.selectedState.cells) return; self.selectedState.startX = self.selectedState.endX = Math[type]?.(...self.selectedState.cells.map(v => v[1])) ?? 0 self.selectedState.startY = self.selectedState.endY = Math[type]?.(...self.selectedState.cells.map(v => v[0])) ?? 0 self.selectedState.cells = undefined } function tranRowsToRange(type: 'min' | 'max') { if (!self.selectedState.rows) return; self.selectedState.startY = Math[type]?.(...self.selectedState.rows) ?? 0 self.selectedState.endY = Math[type]?.(...self.selectedState.rows) ?? 0 self.selectedState.startX = self.selectedState.endX = -1; self.selectedState.rows = undefined } this.callbackSelected(); e.preventDefault(); this.throttleIntoViewFirstSelected() return false; } private intoViewFirstSelected(){ let startX let startY let node if(this.selectedState.cells){ startX = Math.min(...this.selectedState.cells.map(([rowindex, colindex]) => colindex)) startY = Math.min(...this.selectedState.cells.map(([rowindex, colindex]) => rowindex)) } else if(this.selectedState.rows){ startX = Math.min(...this.selectedState.rows) } else if (this.selectedState.startY !== undefined){ startX = this.getMinStartX() startY = this.getMinStartY() } if(startY === -1){ node = this.getCellElement({colindex: startX}) }else if(startX === -1){ node = this.getCellElement({rowindex: startY}) }else if(startY !== undefined){ node = this.getCellElement({rowindex: startY, colindex: startX}) } node?.scrollIntoView({block: 'center'}) } protected checkAllRows() { this.resetSelectedState(); this.selectedState.selectedTh = true; this.selectedState.startX = 0; this.selectedState.endX = (this.props.maxColSelection ?? this.tileColumns.value.length) - 1; this.selectedState.startY = -this.columnGroup.value.length; this.selectedState.endY = (this.props.maxRowSelection ?? this.dataSource.value.length) - 1; if (typeof this.props.customEditer?.onSelectedAllHandel === 'function'){ this.props.customEditer?.onSelectedAllHandel(this) } this.callbackSelected() } private keypressCtrlA() { if (!this.currentView) return false; if ((event.ctrlKey || event.metaKey) && event.keyCode === 65) { event.preventDefault(); this.checkAllRows(); return false; } } resetSelectedState(){ this.selectedState.selectedTh = false this.selectedState.endX = this.selectedState.startX = this.selectedState.endY = this.selectedState.startY = this.selectedState.cells = this.selectedState.rows = undefined; } private listenResetSelectedState(){ if(this.props.customEditer?.disabledOuterClickClearSelected) return; const documentClickHandel = (e: any) => { // @ts-ignore if (this.showMenus.value) return; if (e.button === 2) return; if (e.ctrlKey || e.metaKey) return; let node = e.target; while (node && node !== document.body) { if (node.tagName === 'TABLE') return false; node = node.parentElement; } this.resetSelectedState() document.removeEventListener('click', documentClickHandel); }; document.removeEventListener('click', documentClickHandel); document.addEventListener('click', documentClickHandel); } private getSelectColumns(){ let columns: Column[] = []; if (this.selectedState.rows){ columns = this.tileColumns.value } else if (this.selectedState.cells){ const colindexs = this.selectedState.cells?.map(v => v[1]) ?? [] columns = this.tileColumns.value.filter((v, colindex: number) => { return colindexs.includes(colindex); }) } else if (this.selectedState?.startX !== undefined && this.selectedState?.startX >= 0) { columns = this.tileColumns.value.filter((v, colindex: number) => { return colindex >= this.getMinStartX() && colindex <= this.getMaxEndX(); }) } return columns } protected getSelectRowindexs(): number[] { let rowindexs = []; if (this.selectedState.cells) { const indexs: number[] = this.selectedState.cells?.map(v => v[0]) ?? [] rowindexs = Array.from(new Set(indexs)) } else if (this.selectedState.rows) { rowindexs = this.selectedState.rows } else if (this.selectedState.startY !== undefined) { let startY = this.getMinStartY() while(startY <= this.getMaxEndY()){ rowindexs.push(startY) startY++ } } return rowindexs } protected getSelectRows(){ const rowindexs = this.getSelectRowindexs() const records = this.dataSource.value.filter((_, rowindex) => rowindexs.includes(rowindex)) return records; } protected callbackSelected(){ if(this.props.openUndo && this.emptyHistoryRecord()) this.addHistoryRecord(); if(typeof this.props.customEditer?.onSelected === 'function'){ this.props.customEditer?.onSelected(this.selectAreasData.value, this) } if (this.selectedState.startX === this.selectedState.endX && this.selectedState.startY === this.selectedState.endY){ this.tryValidateCell?.(this.selectedState.startY, this.selectedState.startX, 'selected'); } } private anyWhereSelectedCallbackSelected(){ if (event.ctrlKey || event.metaKey || event.key === 'Control'){ this.callbackSelected() } } }