fix: 列尾按住拖拽图标不松反复进行增删列时,计数结果不准

This commit is contained in:
jingrow 2026-06-07 00:45:16 +08:00
parent 7c26b9f1c2
commit 0c3697b405
2 changed files with 72 additions and 48 deletions

View File

@ -321,37 +321,44 @@ export function useTableHandle(editorRef: Ref<Editor | null>) {
function addColRight() { execCmd('addColumnAfter') }
/**
*
* posAtCoords
* selection
*
* posAtCoords CellSelection
* editor.chain() .focus() CellSelection TextSelection
*
* @returns
*/
function execTableCmdAtCell(name: string, targetCellSelector: string) {
function execTableCmdAtCell(name: string, targetCellSelector: string): boolean {
const editor = editorRef.value
if (!editor?.view) return
if (!editor?.view) return false
const view = editor.view
const table = getTableEl()
if (!table) return
if (!table) return false
const cell = table.querySelector(targetCellSelector) as HTMLElement | null
if (!cell) return
if (!cell) return false
const rect = cell.getBoundingClientRect()
const coords = view.posAtCoords({ left: rect.left + 2, top: rect.top + 2 })
if (!coords) return
if (!coords) return false
const $pos = view.state.doc.resolve(coords.pos)
const $cell = cellAround($pos)
if (!$cell) return
if (!$cell) return false
// 把 selection 放到目标单元格
const tr = view.state.tr
tr.setSelection(new CellSelection($cell))
view.dispatch(tr)
// 先 focus view再设置 CellSelection
view.focus()
view.dispatch(view.state.tr.setSelection(new CellSelection($cell)))
// 从已更新 state 创建 chain 执行命令
// 注意:不使用 .focus(),避免将 CellSelection 转为 TextSelection
const chain = editor.chain()
const cmd = (chain as any)[name]
if (typeof cmd !== 'function') return false
const result = cmd().run()
// 执行命令并刷新 tableBbox 使加号条跟随表格新尺寸
execCmd(name)
refreshTableBbox()
closeAllMenus()
return result
}
/** 刷新 tableBbox 使加号条跟表格新尺寸 */
@ -366,40 +373,41 @@ export function useTableHandle(editorRef: Ref<Editor | null>) {
}
/** 在最后一行下方加行 */
function addRowBelowSticky() {
execTableCmdAtCell('addRowAfter', 'tbody tr:last-child td, tbody tr:last-child th')
function addRowBelowSticky(): boolean {
return execTableCmdAtCell('addRowAfter', 'tbody tr:last-child td, tbody tr:last-child th')
}
/** 在最后一列右侧加列 */
function addColRightSticky() {
execTableCmdAtCell('addColumnAfter', 'tbody tr:last-child td:last-child, tbody tr:last-child th:last-child')
function addColRightSticky(): boolean {
return execTableCmdAtCell('addColumnAfter', 'tbody tr:last-child td:last-child, tbody tr:last-child th:last-child')
}
/** 删除最后一行(向上拖拽时使用) */
function deleteLastRow() {
function deleteLastRow(): boolean {
const editor = editorRef.value
if (!editor?.view) return
if (!editor?.view) return false
const table = getTableEl()
if (table) {
const rows = table.querySelectorAll('tbody tr')
if (rows.length <= 1) return
if (rows.length <= 1) return false
}
execTableCmdAtCell('deleteRow', 'tbody tr:last-child td, tbody tr:last-child th')
return execTableCmdAtCell('deleteRow', 'tbody tr:last-child td, tbody tr:last-child th')
}
/** 删除最后一列(向左拖拽时使用) */
function deleteLastCol() {
function deleteLastCol(): boolean {
const editor = editorRef.value
if (!editor?.view) return
if (!editor?.view) return false
const table = getTableEl()
if (table) {
const firstRow = table.querySelector('tbody tr:first-child')
if (firstRow) {
const cells = firstRow.querySelectorAll('td, th')
if (cells.length <= 1) return
}
if (!table) return false
// 与 deleteLastRow 保持对称:检查所有行中最少列数,防止删除最后一列
const rows = table.querySelectorAll('tbody tr')
let minColCount = Infinity
for (const row of rows) {
minColCount = Math.min(minColCount, row.querySelectorAll('td, th').length)
}
execTableCmdAtCell('deleteColumn', 'tbody tr:last-child td:last-child, tbody tr:last-child th:last-child')
if (minColCount <= 1) return false
return execTableCmdAtCell('deleteColumn', 'tbody tr:last-child td:last-child, tbody tr:last-child th:last-child')
}
/** 删除最后一行(向上拖拽时使用) */

View File

@ -197,6 +197,8 @@ function onAddColStripMouseDown(e: MouseEvent) {
/** 拖拽启动阈值px超过此距离才认为是在拖拽 */
const DRAG_THRESHOLD = 5
/** 单次 mousemove 最多增删数量,防止死循环 */
const MAX_DRAG_ITERATIONS = 50
function onDragAddRow(e: MouseEvent) {
if (!_isDragging.value && Math.abs(e.clientY - _dragStartY.value) < DRAG_THRESHOLD) return
@ -204,15 +206,22 @@ function onDragAddRow(e: MouseEvent) {
const deltaY = e.clientY - _dragStartY.value
const targetCount = deltaY >= 0 ? Math.floor(deltaY / 28) : -Math.floor(-deltaY / 28)
// Add rows when target exceeds current net count
while (targetCount > _netRowCount.value) {
_netRowCount.value++
addRowBelowSticky()
//
let safety = 0
while (targetCount > _netRowCount.value && safety++ < MAX_DRAG_ITERATIONS) {
if (addRowBelowSticky()) {
_netRowCount.value++
} else {
break
}
}
// Remove rows when target falls below current net count (undo additions or delete originals)
while (targetCount < _netRowCount.value) {
_netRowCount.value--
deleteLastRow()
safety = 0
while (targetCount < _netRowCount.value && safety++ < MAX_DRAG_ITERATIONS) {
if (deleteLastRow()) {
_netRowCount.value--
} else {
break
}
}
updateOverlayRect()
}
@ -223,15 +232,22 @@ function onDragAddCol(e: MouseEvent) {
const deltaX = e.clientX - _dragStartX.value
const targetCount = deltaX >= 0 ? Math.floor(deltaX / 28) : -Math.floor(-deltaX / 28)
// Add columns when target exceeds current net count
while (targetCount > _netColCount.value) {
_netColCount.value++
addColRightSticky()
//
let safety = 0
while (targetCount > _netColCount.value && safety++ < MAX_DRAG_ITERATIONS) {
if (addColRightSticky()) {
_netColCount.value++
} else {
break
}
}
// Remove columns when target falls below current net count (undo additions or delete originals)
while (targetCount < _netColCount.value) {
_netColCount.value--
deleteLastCol()
safety = 0
while (targetCount < _netColCount.value && safety++ < MAX_DRAG_ITERATIONS) {
if (deleteLastCol()) {
_netColCount.value--
} else {
break
}
}
updateOverlayRect()
}