由 OCR(open-codereview.ai,deepseek-v4-flash)审查驱动修复: - Roles 多组勾选、Schedules 月视图新建、useDirtyGuard 稳定引用 - JinshujuMatchModal success:false、Attendance store 订阅、EditableCell Enter、DynamicChart 空数据、渲染期 ref - AttachmentsTab 附件预览不再 window.open(防存储型 XSS)+ 请求乱序防护 - 考勤 late 计入出勤率与主状态 Reviewed-by: OCR (open-codereview.ai)
54 lines
1.8 KiB
TypeScript
54 lines
1.8 KiB
TypeScript
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 改用确认关闭
|
|
* <Modal onCancel={() => confirmClose(() => setOpen(false))} ...>
|
|
*/
|
|
export function useDirtyGuard(form: FormInstance) {
|
|
const { modal } = App.useApp();
|
|
const pristineRef = useRef<unknown>(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;
|