20260812172705
This commit is contained in:
1 parent
1d7f7c2bba
commit
87b2d3c482
1 file changed
+60
-18
@@ -66,6 +66,8 @@ const rowEditingStates = new WeakMap();
|
|||||||
let activeEditingRow = null;
|
let activeEditingRow = null;
|
||||||
let pendingEditingTarget = null;
|
let pendingEditingTarget = null;
|
||||||
const pressedNavigationKeys = new Set();
|
const pressedNavigationKeys = new Set();
|
||||||
|
let editRequestId = 0;
|
||||||
|
let currentTableRow = null;
|
||||||
|
|
||||||
function getRowKey(row) {
|
function getRowKey(row) {
|
||||||
return typeof props.rowKey === "function" ? props.rowKey(row) : row?.[props.rowKey];
|
return typeof props.rowKey === "function" ? props.rowKey(row) : row?.[props.rowKey];
|
||||||
@@ -81,6 +83,18 @@ function getRowEditingState(row) {
|
|||||||
return state;
|
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) {
|
function captureEditableValues(row) {
|
||||||
return new Map(
|
return new Map(
|
||||||
props.columns.filter((col) => col.editor).map((col) => [col.dataIndex, row[col.dataIndex]]),
|
props.columns.filter((col) => col.editor).map((col) => [col.dataIndex, row[col.dataIndex]]),
|
||||||
@@ -248,10 +262,7 @@ function onColumnsUpdate(columns) {
|
|||||||
const real = (columns || [])
|
const real = (columns || [])
|
||||||
.filter(
|
.filter(
|
||||||
(c) =>
|
(c) =>
|
||||||
!isSelectionColumn(c) &&
|
!isSelectionColumn(c) && c.type !== "seq" && c.type !== "dragRow" && c.dataIndex !== "",
|
||||||
c.type !== "seq" &&
|
|
||||||
c.type !== "dragRow" &&
|
|
||||||
c.dataIndex !== "",
|
|
||||||
)
|
)
|
||||||
.map((col) => {
|
.map((col) => {
|
||||||
const original = originalByKey.get(String(col.key ?? col.dataIndex ?? ""));
|
const original = originalByKey.get(String(col.key ?? col.dataIndex ?? ""));
|
||||||
@@ -301,18 +312,32 @@ async function scrollToRow(rowKey) {
|
|||||||
// 程序化激活行编辑。新增行等场景默认先滚动到目标行;行内操作可传
|
// 程序化激活行编辑。新增行等场景默认先滚动到目标行;行内操作可传
|
||||||
// { scroll: false } 保持当前滚动位置。
|
// { scroll: false } 保持当前滚动位置。
|
||||||
async function editRow(rowKey, focusCol = null, options = {}) {
|
async function editRow(rowKey, focusCol = null, options = {}) {
|
||||||
|
const requestId = ++editRequestId;
|
||||||
if (options.scroll === false) await nextTick();
|
if (options.scroll === false) await nextTick();
|
||||||
else await scrollToRow(rowKey);
|
else await scrollToRow(rowKey);
|
||||||
|
if (requestId !== editRequestId) return;
|
||||||
await nextTick();
|
await nextTick();
|
||||||
|
if (requestId !== editRequestId) return;
|
||||||
const row = props.dataSource.find((item) => String(getRowKey(item)) === String(rowKey));
|
const row = props.dataSource.find((item) => String(getRowKey(item)) === String(rowKey));
|
||||||
if (!row) return;
|
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);
|
activateEditing(row, focusCol);
|
||||||
|
lastEditActivateAt = now;
|
||||||
|
|
||||||
const focusColumn = displayColumns.value.find((column) => column.dataIndex === focusCol);
|
const focusColumn = displayColumns.value.find((column) => column.dataIndex === focusCol);
|
||||||
if (focusColumn) {
|
if (focusColumn) {
|
||||||
table?.setAreaSelection?.(
|
tableRef.value?.setAreaSelection?.(
|
||||||
{ begin: { row, col: focusColumn } },
|
{ begin: { row, col: focusColumn } },
|
||||||
{ silent: true, scrollToView: false },
|
{ silent: true, scrollToView: false },
|
||||||
);
|
);
|
||||||
@@ -352,12 +377,12 @@ function clearEditActivateTimer() {
|
|||||||
|
|
||||||
// 键盘导航停止(松开按键或停顿)后再激活编辑器,合并连续/按住按键,
|
// 键盘导航停止(松开按键或停顿)后再激活编辑器,合并连续/按住按键,
|
||||||
// 避免滚动过程中每帧创建/销毁编辑器组件导致掉帧。
|
// 避免滚动过程中每帧创建/销毁编辑器组件导致掉帧。
|
||||||
function scheduleEditActivation() {
|
function scheduleEditActivation(delay = 100) {
|
||||||
clearEditActivateTimer();
|
clearEditActivateTimer();
|
||||||
editActivateTimer = setTimeout(() => {
|
editActivateTimer = setTimeout(() => {
|
||||||
editActivateTimer = null;
|
editActivateTimer = null;
|
||||||
commitPendingEditing();
|
commitPendingEditing();
|
||||||
}, 100);
|
}, delay);
|
||||||
}
|
}
|
||||||
|
|
||||||
function commitPendingEditing() {
|
function commitPendingEditing() {
|
||||||
@@ -368,8 +393,19 @@ function commitPendingEditing() {
|
|||||||
(item) => item === target.row || String(getRowKey(item)) === String(getRowKey(target.row)),
|
(item) => item === target.row || String(getRowKey(item)) === String(getRowKey(target.row)),
|
||||||
);
|
);
|
||||||
if (row) {
|
if (row) {
|
||||||
tableRef.value?.setCurrentRow?.(row, { silent: true });
|
setCurrentRow(row);
|
||||||
activateEditing(row, target.focusCol);
|
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();
|
lastEditActivateAt = performance.now();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -447,7 +483,7 @@ function handleAreaSelectionChange(ranges) {
|
|||||||
pendingEditingTarget = null;
|
pendingEditingTarget = null;
|
||||||
clearEditActivateTimer();
|
clearEditActivateTimer();
|
||||||
activateEditing(row, focusCol);
|
activateEditing(row, focusCol);
|
||||||
tableRef.value?.setCurrentRow?.(row, { silent: true });
|
setCurrentRow(row);
|
||||||
lastEditActivateAt = now;
|
lastEditActivateAt = now;
|
||||||
} else {
|
} else {
|
||||||
pendingEditingTarget = { row, focusCol };
|
pendingEditingTarget = { row, focusCol };
|
||||||
@@ -457,9 +493,8 @@ function handleAreaSelectionChange(ranges) {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
// 非键盘导航(鼠标点击等):立即激活。
|
// 鼠标点击为离散意图操作,期望即时聚焦,不做连点合并。
|
||||||
// 同一行已编辑时仍更新 focusCol(保证焦点切换到新列),
|
// 同一行内切换列仍立即更新 focusCol,并暂时禁用 select 全选。
|
||||||
// 但暂时禁用 select 全选(避免鼠标点击触发全选文本)。
|
|
||||||
pendingEditingTarget = null;
|
pendingEditingTarget = null;
|
||||||
clearEditActivateTimer();
|
clearEditActivateTimer();
|
||||||
if (activeEditingRow === row) {
|
if (activeEditingRow === row) {
|
||||||
@@ -480,11 +515,11 @@ watch(
|
|||||||
rebindActiveEditingRow(props.dataSource);
|
rebindActiveEditingRow(props.dataSource);
|
||||||
await nextTick();
|
await nextTick();
|
||||||
if (rowKey == null) {
|
if (rowKey == null) {
|
||||||
tableRef.value?.setCurrentRow?.(undefined, { silent: true });
|
setCurrentRow(undefined);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
const row = props.dataSource.find((item) => String(getRowKey(item)) === String(rowKey));
|
const row = props.dataSource.find((item) => String(getRowKey(item)) === String(rowKey));
|
||||||
tableRef.value?.setCurrentRow?.(row, { silent: true });
|
setCurrentRow(row);
|
||||||
},
|
},
|
||||||
{ immediate: true },
|
{ immediate: true },
|
||||||
);
|
);
|
||||||
@@ -493,6 +528,7 @@ watch(
|
|||||||
() => props.editable,
|
() => props.editable,
|
||||||
(editable) => {
|
(editable) => {
|
||||||
if (editable) return;
|
if (editable) return;
|
||||||
|
editRequestId += 1;
|
||||||
pendingEditingTarget = null;
|
pendingEditingTarget = null;
|
||||||
removeNavigationListeners();
|
removeNavigationListeners();
|
||||||
pressedNavigationKeys.clear();
|
pressedNavigationKeys.clear();
|
||||||
@@ -504,6 +540,7 @@ watch(
|
|||||||
);
|
);
|
||||||
|
|
||||||
onBeforeUnmount(() => {
|
onBeforeUnmount(() => {
|
||||||
|
editRequestId += 1;
|
||||||
removeNavigationListeners();
|
removeNavigationListeners();
|
||||||
clearEditActivateTimer();
|
clearEditActivateTimer();
|
||||||
pressedNavigationKeys.clear();
|
pressedNavigationKeys.clear();
|
||||||
@@ -545,6 +582,7 @@ defineExpose({
|
|||||||
:show-overflow="showOverflow"
|
:show-overflow="showOverflow"
|
||||||
:area-selection="editable"
|
:area-selection="editable"
|
||||||
:bordered="bordered"
|
:bordered="bordered"
|
||||||
|
@current-change="handleCurrentChange"
|
||||||
@area-selection-change="handleAreaSelectionChange"
|
@area-selection-change="handleAreaSelectionChange"
|
||||||
@row-order-change="handleRowOrderChange"
|
@row-order-change="handleRowOrderChange"
|
||||||
>
|
>
|
||||||
@@ -591,11 +629,16 @@ defineExpose({
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
height: 100%;
|
height: 100%;
|
||||||
}
|
}
|
||||||
|
.stk-table tbody tr {
|
||||||
|
transform: translateZ(0);
|
||||||
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
<!-- 非 scoped 全局样式:原生 checkbox 保持低组件开销,外观使用 shadcn 语义变量。 -->
|
<!-- 非 scoped 全局样式:原生 checkbox 保持低组件开销,外观使用 shadcn 语义变量。 -->
|
||||||
<style lang="scss">
|
<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 权重 (0,2,1) > 库默认 .stk-table (0,1,1) */
|
||||||
.fms-table__inner.stk-table {
|
.fms-table__inner.stk-table {
|
||||||
th,
|
th,
|
||||||
@@ -767,7 +810,6 @@ defineExpose({
|
|||||||
white-space: nowrap;
|
white-space: nowrap;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
// actions 列按钮容器
|
// actions 列按钮容器
|
||||||
.fms-actions-cell {
|
.fms-actions-cell {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
|
|||||||
Reference in new issue
Block a user