refactor(web): 系统设置模块页面表单一致性重构
- 新增 useCrudDrawer hook 封装 CRUD Drawer 通用模式(状态管理/提交/错误处理) - 新增 useListData hook 封装非分页列表数据获取 - 11 个页面统一迁移到 DrawerForm + 共享 hooks,消除重复代码 - 错误处理统一使用 useApiRequest.execute(),移除内联 try-catch - Modal 全部替换为 DrawerForm,保持 UI 一致性 - 净减少 ~1300 行代码(858 增 / 2136 删)
This commit is contained in:
74
apps/web/src/hooks/useCrudDrawer.ts
Normal file
74
apps/web/src/hooks/useCrudDrawer.ts
Normal file
@@ -0,0 +1,74 @@
|
||||
import { useState, useCallback } from 'react';
|
||||
import { useApiRequest } from './useApiRequest';
|
||||
|
||||
export interface UseCrudDrawerOptions<T> {
|
||||
getId: (record: T) => string;
|
||||
onCreate: (values: Record<string, unknown>) => Promise<void>;
|
||||
onUpdate: (id: string, values: Record<string, unknown> & { version: number }) => Promise<void>;
|
||||
onSuccess?: () => void;
|
||||
}
|
||||
|
||||
export interface UseCrudDrawerReturn<T> {
|
||||
open: boolean;
|
||||
editingRecord: T | null;
|
||||
initialValues: Record<string, unknown> | undefined;
|
||||
openCreate: (defaults?: Record<string, unknown>) => void;
|
||||
openEdit: (record: T, fieldMap?: (record: T) => Record<string, unknown>) => void;
|
||||
close: () => void;
|
||||
handleSubmit: (values: Record<string, unknown>) => Promise<void>;
|
||||
loading: boolean;
|
||||
}
|
||||
|
||||
export function useCrudDrawer<T extends { version: number }>(
|
||||
options: UseCrudDrawerOptions<T>,
|
||||
): UseCrudDrawerReturn<T> {
|
||||
const [open, setOpen] = useState(false);
|
||||
const [editingRecord, setEditingRecord] = useState<T | null>(null);
|
||||
const [initialValues, setInitialValues] = useState<Record<string, unknown> | undefined>(undefined);
|
||||
const { execute, loading } = useApiRequest();
|
||||
|
||||
const openCreate = useCallback((defaults?: Record<string, unknown>) => {
|
||||
setEditingRecord(null);
|
||||
setInitialValues(defaults);
|
||||
setOpen(true);
|
||||
}, []);
|
||||
|
||||
const openEdit = useCallback((record: T, fieldMap?: (record: T) => Record<string, unknown>) => {
|
||||
setEditingRecord(record);
|
||||
setInitialValues(fieldMap ? fieldMap(record) : (record as unknown as Record<string, unknown>));
|
||||
setOpen(true);
|
||||
}, []);
|
||||
|
||||
const close = useCallback(() => {
|
||||
setOpen(false);
|
||||
setEditingRecord(null);
|
||||
setInitialValues(undefined);
|
||||
}, []);
|
||||
|
||||
const handleSubmit = useCallback(
|
||||
async (values: Record<string, unknown>) => {
|
||||
if (editingRecord) {
|
||||
await execute(
|
||||
() => options.onUpdate(options.getId(editingRecord), { ...values, version: (editingRecord as unknown as { version: number }).version }),
|
||||
'更新成功',
|
||||
);
|
||||
} else {
|
||||
await execute(() => options.onCreate(values), '创建成功');
|
||||
}
|
||||
close();
|
||||
options.onSuccess?.();
|
||||
},
|
||||
[editingRecord, options, close, execute],
|
||||
);
|
||||
|
||||
return {
|
||||
open,
|
||||
editingRecord,
|
||||
initialValues,
|
||||
openCreate,
|
||||
openEdit,
|
||||
close,
|
||||
handleSubmit,
|
||||
loading,
|
||||
};
|
||||
}
|
||||
33
apps/web/src/hooks/useListData.ts
Normal file
33
apps/web/src/hooks/useListData.ts
Normal file
@@ -0,0 +1,33 @@
|
||||
import { useState, useCallback, useEffect, useRef } from 'react';
|
||||
|
||||
export interface UseListDataReturn<T> {
|
||||
data: T[];
|
||||
loading: boolean;
|
||||
refresh: () => Promise<void>;
|
||||
}
|
||||
|
||||
export function useListData<T>(fetchFn: () => Promise<T[]>, autoFetch = true): UseListDataReturn<T> {
|
||||
const [data, setData] = useState<T[]>([]);
|
||||
const [loading, setLoading] = useState(false);
|
||||
const fetchFnRef = useRef(fetchFn);
|
||||
fetchFnRef.current = fetchFn;
|
||||
|
||||
const refresh = useCallback(async () => {
|
||||
setLoading(true);
|
||||
try {
|
||||
const result = await fetchFnRef.current();
|
||||
setData(result);
|
||||
} catch {
|
||||
setData([]);
|
||||
}
|
||||
setLoading(false);
|
||||
}, []);
|
||||
|
||||
useEffect(() => {
|
||||
if (autoFetch) {
|
||||
refresh();
|
||||
}
|
||||
}, [refresh, autoFetch]);
|
||||
|
||||
return { data, loading, refresh };
|
||||
}
|
||||
Reference in New Issue
Block a user