refactor(admin): 用 usehooks-ts useEventListener/useTimeout 与 dayjs 替代手写监听、撤销计时与相对时间
This commit is contained in:
@@ -1,6 +1,7 @@
|
||||
import { useEffect, useState } from 'react';
|
||||
import { Button, Tooltip } from 'antd';
|
||||
import { VerticalAlignTopOutlined } from '@ant-design/icons';
|
||||
import { useEventCallback, useEventListener } from 'usehooks-ts';
|
||||
|
||||
/**
|
||||
* 全局「回到顶部」浮动按钮:长列表滚动超过 400px 后出现。
|
||||
@@ -8,13 +9,13 @@ import { VerticalAlignTopOutlined } from '@ant-design/icons';
|
||||
*/
|
||||
export const BackTop: React.FC<{ threshold?: number }> = ({ threshold = 400 }) => {
|
||||
const [visible, setVisible] = useState(false);
|
||||
const updateVisible = useEventCallback(() => setVisible(window.scrollY > threshold));
|
||||
|
||||
useEffect(() => {
|
||||
const onScroll = () => setVisible(window.scrollY > threshold);
|
||||
onScroll();
|
||||
window.addEventListener('scroll', onScroll, { passive: true });
|
||||
return () => window.removeEventListener('scroll', onScroll);
|
||||
}, [threshold]);
|
||||
updateVisible();
|
||||
}, [threshold, updateVisible]);
|
||||
|
||||
useEventListener('scroll', updateVisible, undefined, { passive: true });
|
||||
|
||||
if (!visible) return null;
|
||||
|
||||
|
||||
@@ -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 { useTimeout } from 'usehooks-ts';
|
||||
import { useEditableCellStore } from '../../store/editableCell/editableCellStore';
|
||||
import { message } from '../../ui/app-message';
|
||||
import './style.css';
|
||||
@@ -102,16 +103,10 @@ const EditableCell = <Value,>({
|
||||
);
|
||||
// 保存成功后短暂显示「撤销」入口:记录保存前的序列化旧值
|
||||
const [undoMeta, setUndoMeta] = useState<{ serializedPrevious: unknown } | null>(null);
|
||||
const undoTimerRef = useRef<number | undefined>(undefined);
|
||||
// 撤销入口 6 秒后自动消失;useTimeout 在 undoMeta 置空/组件卸载时自动清理
|
||||
useTimeout(() => setUndoMeta(null), undoMeta ? 6_000 : null);
|
||||
const enabled = !disabled && (!permission || hasPermission(permission));
|
||||
|
||||
useEffect(
|
||||
() => () => {
|
||||
window.clearTimeout(undoTimerRef.current);
|
||||
},
|
||||
[],
|
||||
);
|
||||
|
||||
const original = useMemo(
|
||||
() =>
|
||||
serializeEditableValue(
|
||||
@@ -146,10 +141,8 @@ const EditableCell = <Value,>({
|
||||
await onSave(parseValue ? parseValue(serialized) : (serialized as Value));
|
||||
useEditableCellStore.getState().clearIfActive(idRef.current);
|
||||
setEditing(false);
|
||||
// 提供 6 秒内的撤销入口(把旧值再保存一次)
|
||||
// 提供 6 秒内的撤销入口(把旧值再保存一次);useTimeout 负责到时自动清除
|
||||
setUndoMeta({ serializedPrevious: previousValue });
|
||||
window.clearTimeout(undoTimerRef.current);
|
||||
undoTimerRef.current = window.setTimeout(() => setUndoMeta(null), 6_000);
|
||||
return true;
|
||||
} catch (error) {
|
||||
message.error(getErrorMessage(error, '保存失败'));
|
||||
@@ -210,7 +203,6 @@ const EditableCell = <Value,>({
|
||||
}
|
||||
useEditableCellStore.getState().setActiveCell({ id: idRef.current, save });
|
||||
// 重新进入编辑时清掉上一次的撤销入口
|
||||
window.clearTimeout(undoTimerRef.current);
|
||||
setUndoMeta(null);
|
||||
setDraft(normalizeEditableValue(formatValue ? formatValue(value) : value, editor));
|
||||
setEditing(true);
|
||||
@@ -218,7 +210,6 @@ const EditableCell = <Value,>({
|
||||
|
||||
const handleUndo = async () => {
|
||||
if (!undoMeta) return;
|
||||
window.clearTimeout(undoTimerRef.current);
|
||||
setUndoMeta(null);
|
||||
try {
|
||||
await onSave(
|
||||
|
||||
Reference in New Issue
Block a user