import { useCallback, useMemo, useRef } from 'react'; import { App } from 'antd'; import type { FormInstance } from 'antd'; import equal from 'fast-deep-equal'; /** * 弹窗「未保存内容」保护:关闭弹窗时若表单值已被修改,先确认再关闭, * 避免用户误关丢失已填内容。 * * 用法: * const { confirmClose, snapshot } = useDirtyGuard(form); * // 打开弹窗(或编辑回填后)时调用一次 snapshot() 记录初始值 * const openEdit = () => { form.setFieldsValue(record); snapshot(); setOpen(true); }; * // Modal 的 onCancel 改用确认关闭 * confirmClose(() => setOpen(false))} ...> */ export function useDirtyGuard(form: FormInstance) { const { modal } = App.useApp(); const pristineRef = useRef(null); /** 记录当前表单值为「未修改」基准;打开弹窗/回填后调用 */ const snapshot = useCallback(() => { pristineRef.current = form.getFieldsValue(); }, [form]); /** 表单是否有未保存修改(与 snapshot 时对比) */ const isDirty = useCallback(() => { return !equal(form.getFieldsValue(), pristineRef.current); }, [form]); const confirmClose = useCallback( (close: () => void) => { if (!isDirty()) { close(); return; } modal.confirm({ title: '放弃未保存的修改?', content: '当前表单有未保存的内容,关闭后修改将丢失。', okText: '放弃修改', okButtonProps: { danger: true }, cancelText: '继续编辑', onOk: close, }); }, [isDirty, modal], ); // 用 useMemo 稳定返回对象引用,避免消费方 useEffect 依赖每次渲染都变化 return useMemo(() => ({ confirmClose, snapshot, isDirty }), [confirmClose, snapshot, isDirty]); } export default useDirtyGuard;