diff --git a/src/components/XdTable/index.vue b/src/components/XdTable/index.vue index f143d27c..0ceada02 100644 --- a/src/components/XdTable/index.vue +++ b/src/components/XdTable/index.vue @@ -94,13 +94,11 @@ - -
+
- + -
@@ -268,6 +266,11 @@ export default { var oDiv = el; var tbBodyDiv=oDiv.getElementsByClassName("el-table__body-wrapper")[0]; + var startCell=null; + var endCell=null; + var selectedCells=[]; + var selDiv=null; + // 鼠标按下事件 tbBodyDiv.onmousedown = function (ev) { var evt = window.event || arguments[0]; @@ -288,10 +291,6 @@ export default { const currentCellInfo=getCellPosition(evt.target); vnode.context.currentCellInfo=currentCellInfo; - - // 绑定右键菜单 - bindRightContextMenu(evt); - } return; } @@ -303,16 +302,26 @@ export default { selNodes[i].classList.remove("seled") } } + // 清除粘贴板 + vnode.context.clearBatchSelect(); + + // 获取鼠标按下的单元格 + const cell = evt.target.closest('td'); + if (!cell || cell.classList.contains('el-table__expanded-cell')) return; + + // 获取单元格位置信息 + const cellInfo = getCellInfo(cell); + if (!cellInfo) return; + startCell = cellInfo; + // console.log("===>startCell===>",startCell); vnode.context.contextVisible=false; - // 增加图层 - var xdTableObj = document.getElementById(vnode.context.tableId); - var tbBody1=xdTableObj.getElementsByClassName("el-table__body-wrapper")[0]; + // 增加图层 var selDiv = document.createElement("div"); selDiv.style.cssText = "position:absolute;width:0px;height:0px;font-size:0px;margin:0px;padding:0px;border:2px solid #409eff;background-color:#d9ecff;z-index:1000;filter:alpha(opacity:60);opacity:0.6;display:none;"; selDiv.id = "selectDiv-"+vnode.context.tableId; - tbBody1.appendChild(selDiv); + tbBodyDiv.appendChild(selDiv); // 表格对象 const refTableObj=vnode.context.getInstance(); @@ -350,15 +359,9 @@ export default { ){ vnode.context.isTableFocused=false; vnode.context.mouseInScope=false; - // cancelRightContextMenu(); return; } - - // if(!tbBodyDiv.contains(document.activeElement)){ - // vnode.context.isTableFocused=false; - // vnode.context.mouseInScope=false; - // return; - // } + vnode.context.isTableFocused=true; vnode.context.mouseInScope=true; @@ -367,7 +370,20 @@ export default { // setMouseRowSelect(evt.target); return; } - // + + // 获取鼠标当前所在的单元格 + const cell = evt.target.closest('td'); + if (!cell || cell.classList.contains('el-table__expanded-cell')) return; + + // 获取单元格位置信息 + const cellInfo = getCellInfo(cell); + if (!cellInfo) return; + + // 记录结束单元格 + endCell = cellInfo; + // console.log("===>startCell==endCell=>",startCell,endCell); + // updateSelectionRange(); + if(flag&&vnode.context.mouseflag){ vnode.context.multipleSelection=[]; @@ -402,7 +418,6 @@ export default { var tbBody2=oDiv.getElementsByClassName("el-table__body-wrapper")[0]; var tbHeader2=oDiv.getElementsByClassName("el-table__header-wrapper")[0]; - selDiv.style.left = Math.min(_x, startX)-tbPosRect2.left+tbBody2.scrollLeft + "px"; //表头的高度 selDiv.style.top = Math.min(_y, startY)-tbPosRect2.top - tbHeader2.offsetHeight+tbBody2.scrollTop+ "px"; @@ -465,15 +480,6 @@ export default { vnode.context.clearEventBubble(evt); }; - function bindRightContextMenu(evt){ - var xdTableObj = document.getElementById(vnode.context.tableId); - xdTableObj.addEventListener("contextmenu", vnode.context.handleOnContextMenu); - } - function cancelRightContextMenu() { - var xdTableObj = document.getElementById(vnode.context.tableId); - xdTableObj.removeEventListener('contextmenu',vnode.context.handleOnContextMenu) - // document.oncontextmenu = new Function("event.returnValue=true"); - } // 获取el-table元素的位置信息 function getTablePosition(tbDom) { const tableRect = tbDom.getBoundingClientRect(); @@ -494,6 +500,136 @@ export default { } } + // 获取单元格信息 ------------------ + function getCellInfo(cell) { + if (!cell) return null; + + // 获取行索引(排除表头) + const row = Array.from(cell.closest('tbody').children).indexOf(cell.parentElement); + if (row === -1) return null; + + // 获取列索引 + const column = Array.from(cell.parentElement.children).indexOf(cell); + if (column === -1) return null; + + // 获取单元格内容 + const content = cell.textContent.trim(); + + // 获取对应的属性名 + // const table = vnode.context.$refs.table; + // const columnProperty = table.columns[column].property; + return { row:row, col:column, text:content,element: cell }; + } + + // 根据行列位置获取单元格元素 + function getCellByPosition(rowIndex, columnIndex) { + // const table = vnode.context.$refs.table.elTable; + // const table=vnode.context.$refs.table.$refs.elTable; + // const rows = table.$el.querySelectorAll('tbody tr'); + + // console.log("===>tbBodyDiv===>",tbBodyDiv); + const rows = tbBodyDiv.querySelectorAll('tr'); + + if (rowIndex >= 0 && rowIndex < rows.length) { + const row = rows[rowIndex]; + const cells = row.querySelectorAll('td'); + + if (columnIndex >= 0 && columnIndex < cells.length) { + return cells[columnIndex]; + } + } + + return null; + } + + // 更新选择范围 + function updateSelectionRange(finalize = false) { + if (!startCell || !endCell) return; + + // 计算选择范围 + const startRow = Math.min(startCell.row, endCell.row); + const endRow = Math.max(startCell.row, endCell.row); + const startColumn = Math.min(startCell.column, endCell.column); + const endColumn = Math.max(startCell.column, endCell.column); + + if (finalize) { + // 最终确定选择范围 + clearSelection(); + + // 选择范围内的所有单元格 + for (let row = startRow; row <= endRow; row++) { + for (let col = startColumn; col <= endColumn; col++) { + const cell = getCellByPosition(row, col); + if (cell) { + const cellInfo = getCellInfo(cell); + selectCell(cellInfo); + } + } + } + } else { + // 实时显示选择范围指示器 + const startCell = getCellByPosition(startRow, startColumn); + const endCell = getCellByPosition(endRow, endColumn); + + if (startCell && endCell) { + const startRect = startCell.getBoundingClientRect(); + const endRect = endCell.getBoundingClientRect(); + const tableDom=vnode.context.$refs.table.$refs.elTable.$el; + const tableRect = tableDom.getBoundingClientRect(); + + // console.log("====>updateSelectionRange===>",tableRect,tableDom) + + selDiv.style.left = `${startRect.left - tableRect.left}px`; + selDiv.style.top = `${startRect.top - tableRect.top}px`; + selDiv.style.width = `${endRect.right - startRect.left}px`; + selDiv.style.height = `${endRect.bottom - startRect.top}px`; + selDiv.style.display = 'block'; + } + } + } + + // 检查单元格是否已选中 + function isCellSelected(cellInfo) { + if (!cellInfo) return false; + + return selectedCells.some(cell => + cell.row === cellInfo.row && cell.column === cellInfo.column + ); + } + + // 清除所有选择 + function clearSelection() { + // 移除所有选中样式 + selectedCells.forEach(cell => { + if (cell.element) { + cell.element.classList.remove('seled'); + } + }); + + // 清空选中列表 + selectedCells = []; + + // 重置起始和结束单元格 + startCell = null; + endCell = null; + + } + + // 选择单元格 + function selectCell(cellInfo) { + if (!cellInfo || isCellSelected(cellInfo)) return; + + // 添加到选中列表 + selectedCells.push(cellInfo); + + // console.log("===>selectedCells===>",selectedCells); + + // 添加选中样式 + if (cellInfo.element) { + cellInfo.element.classList.add('seled'); + } + } + function setMouseCellSelect(cell,selRowList) { try{ while(cell.tagName !== 'TD'){ @@ -547,6 +683,12 @@ export default { } //在鼠标抬起后做的重置 document.onmouseup = function () { + + // 如果有结束单元格,更新选择范围 + // if (endCell) { + // updateSelectionRange(true); + // } + //把鼠标移动事初始化 // document.onmousemove=null; var xdTableObj = document.getElementById(vnode.context.tableId); @@ -686,11 +828,6 @@ export default { this.isNeedDbSaveTpl=false; } - // 监听全局焦点变化事件 - // document.addEventListener('focusin', this.handleGlobalFocusIn); - // document.addEventListener('focusout', this.handleGlobalFocusOut); - - this.initKeyDownEvent(); // 初始化默认模版到本地 this.initDefaultTplList(); this.initStorage() @@ -708,28 +845,6 @@ export default { getUUID() { return Math.random().toString(36).substring(3,10); }, - - // 处理全局焦点进入事件 - handleGlobalFocusIn(event) { - // var tableEl = document.getElementById(this.tableId); - // const hasFocus = tableEl.contains(event.target); - // console.log("===>hasFocus===>",hasFocus,tableEl,event.target); - // 表格获得焦点 - // if (hasFocus && !this.isTableFocused) { - // this.tableHasFocus = true; - // this.$message({ - // message: '表格已获得焦点', - // type: 'success', - // duration: 1500 - // }); - // } - }, - - handleXdTableBlur(){ - this.isTableFocused=false; - console.log("===>handleXdTableBlur===>",this.isTableFocused); - }, - // 全局键盘事件处理 handleGlobalKeydown(event) { // 检查是否按下了Ctrl+C组合键 @@ -742,18 +857,21 @@ export default { } } }, + handleOnContextMenu(event){ // vnode.context.clearEventBubble(event) - var xdTableObj = document.getElementById(this.tableId); - this.clearEventBubble(event) - var menu = xdTableObj.querySelector("#contextMenu"); - let tbPosRect=this.getTablePosition(xdTableObj); - menu.style.left = (event.clientX-tbPosRect.left+5) + "px"; - menu.style.top = (event.clientY-tbPosRect.top-35) + "px"; - menu.style.display = "block"; - menu.style.zIndex = 3000; - this.contextVisible=true; - event.preventDefault(); + if(this.isTableFocused){ + var xdTableObj = document.getElementById(this.tableId); + this.clearEventBubble(event) + var menu = xdTableObj.querySelector("#contextMenu"); + let tbPosRect=this.getTablePosition(xdTableObj); + menu.style.left = (event.clientX-tbPosRect.left+5) + "px"; + menu.style.top = (event.clientY-tbPosRect.top-35) + "px"; + menu.style.display = "block"; + menu.style.zIndex = 3000; + this.contextVisible=true; + event.preventDefault(); + } }, clearSelection(){ this.$nextTick(()=>{ @@ -763,9 +881,15 @@ export default { initKeyDownEvent(){ document.addEventListener('keydown', this.handleGlobalKeydown ); + + var xdTableObj = document.getElementById(this.tableId); + xdTableObj.addEventListener("contextmenu", this.handleOnContextMenu); }, removeKeyDownEvent(){ document.removeEventListener('keydown', this.handleGlobalKeydown ); + + var xdTableObj = document.getElementById(this.tableId); + xdTableObj.removeEventListener("contextmenu", this.handleOnContextMenu); }, onTplSelectInputBlur(e) { @@ -929,6 +1053,11 @@ export default { } return params; }, + clearBatchSelect(){ + this.multipleSelection=[]; + this.currentSelectStr=null; + this.clearClipboard(); + }, // 鼠标框选---------- //当批量选中结束调用 handleBatchSelect(){ @@ -988,14 +1117,15 @@ export default { handleCopySelect(event) { this.contextVisible=false; if(this.currentSelectStr){ + let cpStr=this.currentSelectStr; if (navigator.clipboard && window.isSecureContext) { - // navigator clipboard 向剪贴板写文本 - this.$modal.msgSuccess('复制成功') - return navigator.clipboard.writeText(this.currentSelectStr) + // navigator clipboard 向剪贴板写文本 + this.$modal.msgSuccess('复制成功') + return navigator.clipboard.writeText(cpStr) } else { // 创建text area const textArea = document.createElement('textarea') - textArea.value = this.currentSelectStr + textArea.value = cpStr // 使text area不在viewport,同时设置不可见 document.body.appendChild(textArea) textArea.focus() @@ -1009,6 +1139,27 @@ export default { } } }, + // 清除剪切板 + clearClipboard() { + let cpStr=''; + if (navigator.clipboard && window.isSecureContext) { + return navigator.clipboard.writeText(cpStr) + } else { + // 创建text area + const textArea = document.createElement('textarea') + textArea.value = cpStr + // 使text area不在viewport,同时设置不可见 + document.body.appendChild(textArea) + textArea.focus() + textArea.select() + // this.$modal.msgSuccess('复制成功') + return new Promise((res, rej) => { + // 执行复制命令并移除文本框 + document.execCommand('copy') ? res() : rej() + textArea.remove() + }) + } + }, // 选择整行 handleSelectCurrentRow(evt){ this.contextVisible=false; @@ -1085,7 +1236,6 @@ export default { let that=this; this.$nextTick(function () { const wrapperTr = document.querySelector('#'+that.tableId+' .el-table__header-wrapper > table > thead > tr'); - // // console.log("===>wrapperTr==>",wrapperTr); if(wrapperTr){ Sortable.create(wrapperTr, { animation: 100, @@ -1103,7 +1253,7 @@ export default { that.storageList = arr; - console.log("====>setHeaderSortable===>",that.storageList) + // console.log("====>setHeaderSortable===>",that.storageList) this.storeTplToLocal(that.storageList) this.initStorage() this.updateTable() @@ -1314,6 +1464,7 @@ export default { this.config.attrs = this.$attrs this.config.listeners = this.$listeners; + // 通过key值触发表格刷新,避免拖拽由于数组长度一致导致diff算法无法识别 // this.config.key = Math.random() + '' // 控制下表格重新刷新 diff --git a/src/components/XdTable/indexNew.vue b/src/components/XdTable/indexNew.vue index 9f387b1e..1e1ca387 100644 --- a/src/components/XdTable/indexNew.vue +++ b/src/components/XdTable/indexNew.vue @@ -311,12 +311,11 @@ export default { } } - // 获取鼠标按下的单元格 + // 获取鼠标按下的单元格 const cell = evt.target.closest('td'); if (!cell || cell.classList.contains('el-table__expanded-cell')) return; // 获取单元格位置信息 - const cellInfo = getCellInfo(cell); if (!cellInfo) return; startCell = cellInfo;