From 87b2d3c48287f8e586b2db7a8fbc7c7dac6f5844 Mon Sep 17 00:00:00 2001 From: ZhangAo Date: Wed, 12 Aug 2026 17:27:06 +0800 Subject: [PATCH] 20260812172705 --- .../src/components/fms-table/FmsTable.vue | 78 ++++++++++++++----- 1 file changed, 60 insertions(+), 18 deletions(-) diff --git a/code/fms/fms-vue/src/components/fms-table/FmsTable.vue b/code/fms/fms-vue/src/components/fms-table/FmsTable.vue index 9a35b00b..b205d172 100644 --- a/code/fms/fms-vue/src/components/fms-table/FmsTable.vue +++ b/code/fms/fms-vue/src/components/fms-table/FmsTable.vue @@ -66,6 +66,8 @@ const rowEditingStates = new WeakMap(); let activeEditingRow = null; let pendingEditingTarget = null; const pressedNavigationKeys = new Set(); +let editRequestId = 0; +let currentTableRow = null; function getRowKey(row) { return typeof props.rowKey === "function" ? props.rowKey(row) : row?.[props.rowKey]; @@ -81,6 +83,18 @@ function getRowEditingState(row) { return state; } +// StkTable 的当前行更新会触发可见行重算。保持幂等,避免 +// activeRowKey watcher 与 editRow() 同时设置同一个行时重复刷新。 +function setCurrentRow(row) { + if (currentTableRow === row) return; + currentTableRow = row || null; + tableRef.value?.setCurrentRow?.(row, { silent: true }); +} + +function handleCurrentChange(_event, row, { select } = {}) { + currentTableRow = select ? row : null; +} + function captureEditableValues(row) { return new Map( props.columns.filter((col) => col.editor).map((col) => [col.dataIndex, row[col.dataIndex]]), @@ -248,10 +262,7 @@ function onColumnsUpdate(columns) { const real = (columns || []) .filter( (c) => - !isSelectionColumn(c) && - c.type !== "seq" && - c.type !== "dragRow" && - c.dataIndex !== "", + !isSelectionColumn(c) && c.type !== "seq" && c.type !== "dragRow" && c.dataIndex !== "", ) .map((col) => { const original = originalByKey.get(String(col.key ?? col.dataIndex ?? "")); @@ -301,18 +312,32 @@ async function scrollToRow(rowKey) { // 程序化激活行编辑。新增行等场景默认先滚动到目标行;行内操作可传 // { scroll: false } 保持当前滚动位置。 async function editRow(rowKey, focusCol = null, options = {}) { + const requestId = ++editRequestId; if (options.scroll === false) await nextTick(); else await scrollToRow(rowKey); + if (requestId !== editRequestId) return; await nextTick(); + if (requestId !== editRequestId) return; const row = props.dataSource.find((item) => String(getRowKey(item)) === String(rowKey)); if (!row) return; - const table = tableRef.value; - table?.setCurrentRow?.(row, { silent: true }); + + const now = performance.now(); + setCurrentRow(row); + if (options.scroll === false && now - lastEditActivateAt < 150) { + pendingEditingTarget = { row, focusCol, setAreaSelection: true }; + clearActiveEditing(); + scheduleEditActivation(); + return; + } + + pendingEditingTarget = null; + clearEditActivateTimer(); activateEditing(row, focusCol); + lastEditActivateAt = now; const focusColumn = displayColumns.value.find((column) => column.dataIndex === focusCol); if (focusColumn) { - table?.setAreaSelection?.( + tableRef.value?.setAreaSelection?.( { begin: { row, col: focusColumn } }, { silent: true, scrollToView: false }, ); @@ -352,12 +377,12 @@ function clearEditActivateTimer() { // 键盘导航停止(松开按键或停顿)后再激活编辑器,合并连续/按住按键, // 避免滚动过程中每帧创建/销毁编辑器组件导致掉帧。 -function scheduleEditActivation() { +function scheduleEditActivation(delay = 100) { clearEditActivateTimer(); editActivateTimer = setTimeout(() => { editActivateTimer = null; commitPendingEditing(); - }, 100); + }, delay); } function commitPendingEditing() { @@ -368,8 +393,19 @@ function commitPendingEditing() { (item) => item === target.row || String(getRowKey(item)) === String(getRowKey(target.row)), ); if (row) { - tableRef.value?.setCurrentRow?.(row, { silent: true }); + setCurrentRow(row); activateEditing(row, target.focusCol); + if (target.setAreaSelection) { + const focusColumn = displayColumns.value.find( + (column) => column.dataIndex === target.focusCol, + ); + if (focusColumn) { + tableRef.value?.setAreaSelection?.( + { begin: { row, col: focusColumn } }, + { silent: true, scrollToView: false }, + ); + } + } lastEditActivateAt = performance.now(); } } @@ -447,7 +483,7 @@ function handleAreaSelectionChange(ranges) { pendingEditingTarget = null; clearEditActivateTimer(); activateEditing(row, focusCol); - tableRef.value?.setCurrentRow?.(row, { silent: true }); + setCurrentRow(row); lastEditActivateAt = now; } else { pendingEditingTarget = { row, focusCol }; @@ -457,9 +493,8 @@ function handleAreaSelectionChange(ranges) { return; } - // 非键盘导航(鼠标点击等):立即激活。 - // 同一行已编辑时仍更新 focusCol(保证焦点切换到新列), - // 但暂时禁用 select 全选(避免鼠标点击触发全选文本)。 + // 鼠标点击为离散意图操作,期望即时聚焦,不做连点合并。 + // 同一行内切换列仍立即更新 focusCol,并暂时禁用 select 全选。 pendingEditingTarget = null; clearEditActivateTimer(); if (activeEditingRow === row) { @@ -480,11 +515,11 @@ watch( rebindActiveEditingRow(props.dataSource); await nextTick(); if (rowKey == null) { - tableRef.value?.setCurrentRow?.(undefined, { silent: true }); + setCurrentRow(undefined); return; } const row = props.dataSource.find((item) => String(getRowKey(item)) === String(rowKey)); - tableRef.value?.setCurrentRow?.(row, { silent: true }); + setCurrentRow(row); }, { immediate: true }, ); @@ -493,6 +528,7 @@ watch( () => props.editable, (editable) => { if (editable) return; + editRequestId += 1; pendingEditingTarget = null; removeNavigationListeners(); pressedNavigationKeys.clear(); @@ -504,6 +540,7 @@ watch( ); onBeforeUnmount(() => { + editRequestId += 1; removeNavigationListeners(); clearEditActivateTimer(); pressedNavigationKeys.clear(); @@ -545,6 +582,7 @@ defineExpose({ :show-overflow="showOverflow" :area-selection="editable" :bordered="bordered" + @current-change="handleCurrentChange" @area-selection-change="handleAreaSelectionChange" @row-order-change="handleRowOrderChange" > @@ -591,11 +629,16 @@ defineExpose({ width: 100%; height: 100%; } - +.stk-table tbody tr { + transform: translateZ(0); +}