Skip to content

Commit fad8cc7

Browse files
committed
Enhance error handling and loading state management in EditableTable and useEditableArray components. Improve validation error propagation during save and cancel operations, ensuring proper feedback in the form. Update tests to validate new delete functionality and confirm behavior when onDelete returns false, preventing unintended deletions.
1 parent 5b4f24b commit fad8cc7

File tree

2 files changed

+1237
-55
lines changed

2 files changed

+1237
-55
lines changed

src/utils/useEditableArray/index.tsx

Lines changed: 86 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -406,9 +406,18 @@ export function SaveEditableAction<T>(
406406
.flat(1)
407407
.filter(Boolean) as string[];
408408
setLoading(true);
409-
await form.validateFields(namePath, {
410-
recursive: true,
411-
});
409+
try {
410+
await form.validateFields(namePath, {
411+
recursive: true,
412+
});
413+
} catch (error: any) {
414+
setLoading(false);
415+
// 重新抛出验证错误,让表单显示错误信息
416+
// validateFields 抛出错误时,表单会自动设置错误状态并显示错误
417+
// 错误对象包含 errorFields,表单会根据这些字段显示错误
418+
// 确保错误被正确传播,这样表单可以正确显示验证错误
419+
throw error;
420+
}
412421

413422
const fields =
414423
context?.getFieldFormatValue?.(namePath) ||
@@ -457,7 +466,10 @@ export function SaveEditableAction<T>(
457466
e.preventDefault();
458467
try {
459468
await save();
460-
} catch {}
469+
} catch (error) {
470+
// 验证错误会被 form.validateFields 抛出,这里不需要处理
471+
// 错误会被表单自动显示
472+
}
461473
}}
462474
>
463475
{loading ? (
@@ -504,7 +516,10 @@ export const DeleteEditableAction: React.FC<
504516
setLoading(true);
505517
const res = await onDelete?.(recordKey, row);
506518
setLoading(false);
507-
519+
// 如果返回 false,阻止删除操作
520+
if (res === false) {
521+
return false;
522+
}
508523
return res;
509524
} catch (e) {
510525
setLoading(false);
@@ -517,8 +532,11 @@ export const DeleteEditableAction: React.FC<
517532
return children !== false ? (
518533
<Popconfirm
519534
key="delete"
520-
title={deletePopconfirmMessage}
521-
onConfirm={() => onConfirm()}
535+
title={deletePopconfirmMessage || '确定要删除这条记录吗?'}
536+
onConfirm={onConfirm}
537+
getPopupContainer={(triggerNode) =>
538+
triggerNode.parentElement || document.body
539+
}
522540
>
523541
<a>
524542
{loading ? (
@@ -926,16 +944,64 @@ export function useEditableArray<RecordType extends AnyObject>(
926944
}
927945
}
928946

929-
clearEditableState(recordKey);
930-
931-
// 清理 preEditRowRef
947+
// 先清理 preEditRowRef 并重置表单字段,然后再清除编辑状态
948+
// 这样在清除编辑状态前,表单字段已经被清除,表格重新渲染时就不会显示输入框
949+
const originRow = preEditRowRef.current;
932950
if (
933-
preEditRowRef.current &&
934-
props.getRowKey(preEditRowRef.current, -1) === recordKey
951+
originRow &&
952+
props.getRowKey(originRow, -1) === recordKey &&
953+
isInEditableSet
935954
) {
955+
try {
956+
// 尝试通过 formProps.formRef 访问 form
957+
const formRef = props.formProps?.formRef as any;
958+
const form = formRef?.current || props.form;
959+
960+
if (form) {
961+
if (props.tableName) {
962+
// name 模式:重置为原始值
963+
const namePath = [props.tableName, recordKey]
964+
.flat(1)
965+
.filter(Boolean) as string[];
966+
form.setFieldsValue(set({}, namePath, originRow));
967+
} else {
968+
// 非 name 模式:清除该行的所有表单字段
969+
// 在非 name 模式下,表单字段路径是 [recordKey, columnDataIndex]
970+
// 如 [624748504, 'title'],需要清除所有以 recordKey 开头的字段
971+
const recordKeyStr = recordKeyToString(recordKey)?.toString();
972+
if (recordKeyStr) {
973+
try {
974+
// 在非 name 模式下,表单字段以嵌套对象的形式存储
975+
// 比如 { '624748504': { 'title': 'value', 'state': 'value' } }
976+
// 需要清除整个嵌套对象
977+
// 先使用 resetFields 清除字段状态
978+
form.resetFields([[recordKeyStr]]);
979+
980+
// 然后使用 setFieldsValue 清除字段值
981+
// 这样可以确保字段被完全清除,表格重新渲染时不会显示输入框
982+
form.setFieldsValue({
983+
[recordKeyStr]: undefined,
984+
});
985+
} catch (error) {
986+
// 如果清除失败,忽略错误
987+
console.warn(
988+
'Failed to clear form fields in cancelEditable:',
989+
error,
990+
);
991+
}
992+
}
993+
}
994+
}
995+
} catch (error) {
996+
// 如果访问 form 失败,忽略错误
997+
console.warn('Failed to reset form fields in cancelEditable:', error);
998+
}
936999
preEditRowRef.current = null;
9371000
}
9381001

1002+
// 最后清除编辑状态,这样表格会重新渲染,输入框会消失
1003+
clearEditableState(recordKey);
1004+
9391005
return true;
9401006
},
9411007
);
@@ -1104,8 +1170,10 @@ export function useEditableArray<RecordType extends AnyObject>(
11041170
await saveRef.current.save();
11051171
clearEditableState(recordKey);
11061172
return true;
1107-
} catch {
1108-
return false;
1173+
} catch (error) {
1174+
// 验证错误会被 save 方法抛出,这里不捕获,让错误传播
1175+
// 这样表单可以正确显示验证错误
1176+
throw error;
11091177
}
11101178
},
11111179
);
@@ -1295,6 +1363,10 @@ export function useEditableArray<RecordType extends AnyObject>(
12951363
childrenColumnName: props.childrenColumnName || 'children',
12961364
};
12971365
const res = await props?.onDelete?.(recordKey, editRow);
1366+
// 如果 onDelete 返回 false,阻止删除操作
1367+
if (res === false) {
1368+
return false;
1369+
}
12981370
// 不传递 false时,重新form.setFieldsValue同一份静态数据,会导致该行始终处于不可编辑状态
12991371
await cancelEditable(recordKey, false);
13001372
props.setDataSource(editableRowByKey(actionProps, 'delete'));

0 commit comments

Comments
 (0)