20260812172705

This commit is contained in:
oneao committed 2026-08-12 17:27:06 +08:00
1 parent 1d7f7c2bba
commit 87b2d3c482
1 file changed
+60 -18
@@ -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);
}
</style>
<!-- 非 scoped 全局样式:原生 checkbox 保持低组件开销,外观使用 shadcn 语义变量。 -->
<style lang="scss">
.stk-table tbody tr {
transform: translateZ(0);
}
/* 双类选择器 .fms-table__inner.stk-table 权重 (0,2,1) > 库默认 .stk-table (0,1,1) */
.fms-table__inner.stk-table {
th,
@@ -767,7 +810,6 @@ defineExpose({
white-space: nowrap;
}
// actions 列按钮容器
.fms-actions-cell {
display: inline-flex;