refactor(admin): EditableCell 跨单元格协调状态迁入 zustand,替代模块级单例并补单测
This commit is contained in:
@@ -3,6 +3,7 @@ import { DatePicker, Input, InputNumber, Select, Spin, Tooltip } from 'antd';
|
||||
import dayjs, { type Dayjs } from 'dayjs';
|
||||
import equal from 'fast-deep-equal';
|
||||
import { usePermission } from '../../hooks/usePermission';
|
||||
import { useEditableCellStore } from '../../store/editableCell/editableCellStore';
|
||||
import { message } from '../../ui/app-message';
|
||||
import './style.css';
|
||||
import { getErrorMessage } from '../../utils/error';
|
||||
@@ -39,9 +40,6 @@ export interface EditableCellProps<Value = unknown> {
|
||||
onSave: (value: Value) => Promise<void>;
|
||||
}
|
||||
|
||||
let activeCell: { id: string; save: () => Promise<boolean> } | null = null;
|
||||
let replayingOutsideAction = false;
|
||||
|
||||
export function normalizeEditableValue(value: unknown, editor: EditableCellEditor) {
|
||||
if (editor === 'date') return value ? dayjs(value as string) : null;
|
||||
if (editor === 'date-range')
|
||||
@@ -125,7 +123,7 @@ const EditableCell = <Value,>({
|
||||
|
||||
const cancel = useCallback(() => {
|
||||
setDraft(normalizeEditableValue(formatValue ? formatValue(value) : value, editor));
|
||||
if (activeCell?.id === idRef.current) activeCell = null;
|
||||
useEditableCellStore.getState().clearIfActive(idRef.current);
|
||||
setEditing(false);
|
||||
}, [editor, formatValue, value]);
|
||||
|
||||
@@ -138,7 +136,7 @@ const EditableCell = <Value,>({
|
||||
return false;
|
||||
}
|
||||
if (editableValuesEqual(serialized, original)) {
|
||||
if (activeCell?.id === idRef.current) activeCell = null;
|
||||
useEditableCellStore.getState().clearIfActive(idRef.current);
|
||||
setEditing(false);
|
||||
return true;
|
||||
}
|
||||
@@ -146,7 +144,7 @@ const EditableCell = <Value,>({
|
||||
const previousValue = original;
|
||||
try {
|
||||
await onSave(parseValue ? parseValue(serialized) : (serialized as Value));
|
||||
if (activeCell?.id === idRef.current) activeCell = null;
|
||||
useEditableCellStore.getState().clearIfActive(idRef.current);
|
||||
setEditing(false);
|
||||
// 提供 6 秒内的撤销入口(把旧值再保存一次)
|
||||
setUndoMeta({ serializedPrevious: previousValue });
|
||||
@@ -167,16 +165,14 @@ const EditableCell = <Value,>({
|
||||
|
||||
useEffect(() => {
|
||||
const cellId = idRef.current;
|
||||
if (editing && activeCell?.id === cellId) activeCell.save = save;
|
||||
return () => {
|
||||
if (activeCell?.id === cellId) activeCell = null;
|
||||
};
|
||||
if (editing) useEditableCellStore.getState().updateActiveSave(cellId, save);
|
||||
return () => useEditableCellStore.getState().clearIfActive(cellId);
|
||||
}, [editing, save]);
|
||||
|
||||
useEffect(() => {
|
||||
if (!editing) return;
|
||||
const onPointerDown = (event: PointerEvent) => {
|
||||
if (replayingOutsideAction) return;
|
||||
if (useEditableCellStore.getState().replayingOutsideAction) return;
|
||||
if (rootRef.current?.contains(event.target as Node) || isEditorOverlay(event.target)) return;
|
||||
const actionTarget =
|
||||
event.target instanceof Element
|
||||
@@ -192,10 +188,10 @@ const EditableCell = <Value,>({
|
||||
event.stopPropagation();
|
||||
void save().then((saved) => {
|
||||
if (!saved) return;
|
||||
replayingOutsideAction = true;
|
||||
useEditableCellStore.getState().setReplayingOutsideAction(true);
|
||||
actionTarget.click();
|
||||
queueMicrotask(() => {
|
||||
replayingOutsideAction = false;
|
||||
useEditableCellStore.getState().setReplayingOutsideAction(false);
|
||||
});
|
||||
});
|
||||
};
|
||||
@@ -207,11 +203,12 @@ const EditableCell = <Value,>({
|
||||
|
||||
const beginEdit = async () => {
|
||||
if (!enabled || saving) return;
|
||||
const { activeCell } = useEditableCellStore.getState();
|
||||
if (activeCell && activeCell.id !== idRef.current) {
|
||||
const saved = await activeCell.save();
|
||||
if (!saved) return;
|
||||
}
|
||||
activeCell = { id: idRef.current, save };
|
||||
useEditableCellStore.getState().setActiveCell({ id: idRef.current, save });
|
||||
// 重新进入编辑时清掉上一次的撤销入口
|
||||
window.clearTimeout(undoTimerRef.current);
|
||||
setUndoMeta(null);
|
||||
|
||||
Reference in New Issue
Block a user