Files
lingxi/apps/web/src/hooks/queries/use-configs.ts
T
Stev_Wang 3b0a99d91f feat(web): A4 数据字典与系统配置页
背景:用户选定方案 A 主从分栏,A4 需要落地字典与配置页。\n改动:新增 /system/dicts、/system/dicts/:type、/system/settings;主从类型列表 + 三层合并 + 树/词表切换 + 条目/类型弹层;6 组配置 + 命名预览 + 阈值二次确认 + 变更记录;客户字段上限改读 effective config;路由/样式/OpenAPI 类型同步。\n影响面:管理中心新增两个菜单页;客户页上限显示改为配置读取(fallback 共享常量)。\n回滚:回滚本提交并恢复 AppLayout / routes / main 样式 import。\n验证:web typecheck/lint/build;单测 154;浏览器自检 1440/1024 无溢出。
2026-11-04 11:49:32 +08:00

109 lines
3.4 KiB
TypeScript

/**
* A4 系统配置查询与写操作。
*
* R-26:阈值 / 上限只存系统配置一处。`useEffectiveConfigsQuery` 供业务页读取前端上限,
* 读取失败或未登录时由调用方回退到 shared 常量(仅作展示兜底,后端仍做最终校验)。
*/
import {
CUSTOMER_BASE_FIELD_LIMIT,
CUSTOMER_GEN_FIELD_LIMIT,
type SystemConfig,
type SystemConfigChange,
type SystemConfigHistoryQuery,
type SystemConfigValue,
type UpdateSystemConfigsInput,
} from '@lingxi/shared';
import {
useMutation,
type UseMutationResult,
useQuery,
useQueryClient,
type UseQueryResult,
} from '@tanstack/react-query';
import {
fetchEffectiveSystemConfigs,
fetchSystemConfigHistory,
fetchSystemConfigs,
updateSystemConfigs,
} from '@/api/configs';
import { queryKeys } from '@/api/query-keys';
export function useSystemConfigsQuery(): UseQueryResult<{ items: SystemConfig[] }> {
return useQuery({
queryKey: queryKeys.systemConfigs,
queryFn: fetchSystemConfigs,
staleTime: 10_000,
});
}
/** 生效配置:key → value(仅登录可读,失败不阻塞业务页) */
export function useEffectiveConfigsQuery(): UseQueryResult<Record<string, SystemConfigValue>> {
return useQuery({
queryKey: queryKeys.systemConfigsEffective,
queryFn: fetchEffectiveSystemConfigs,
select: (data) => {
const values: Record<string, SystemConfigValue> = {};
for (const item of data.items) {
values[item.key] = item.value;
}
return values;
},
staleTime: 60_000,
retry: 1,
});
}
/** 读取单个数值型生效配置;缺失 / 非数字时回退 shared 常量 */
export function useEffectiveNumberConfig(key: string, fallback: number): number {
const effective = useEffectiveConfigsQuery();
const value = effective.data?.[key];
return typeof value === 'number' ? value : fallback;
}
export interface CustomerFieldLimits {
baseFieldLimit: number;
genFieldLimit: number;
}
/** 客户基础字段 / 进入生成字段上限(A3 客户页与表单校验共用,R-26) */
export function useCustomerFieldLimits(): CustomerFieldLimits {
const effective = useEffectiveConfigsQuery();
const base = effective.data?.['limit.customer.base_field'];
const gen = effective.data?.['limit.customer.gen_field'];
return {
baseFieldLimit: typeof base === 'number' ? base : CUSTOMER_BASE_FIELD_LIMIT,
genFieldLimit: typeof gen === 'number' ? gen : CUSTOMER_GEN_FIELD_LIMIT,
};
}
export function useSystemConfigHistoryQuery(
query: Partial<SystemConfigHistoryQuery> = {},
options: { enabled?: boolean } = {},
): UseQueryResult<{ items: SystemConfigChange[]; next_cursor: string | null }> {
return useQuery({
queryKey: queryKeys.systemConfigHistory(query),
queryFn: () => fetchSystemConfigHistory(query),
enabled: options.enabled ?? true,
staleTime: 5_000,
});
}
export function useUpdateSystemConfigsMutation(): UseMutationResult<
{ items: SystemConfig[] },
Error,
UpdateSystemConfigsInput
> {
const queryClient = useQueryClient();
return useMutation({
mutationFn: updateSystemConfigs,
onSuccess: async () => {
await Promise.all([
queryClient.invalidateQueries({ queryKey: queryKeys.systemConfigs }),
queryClient.invalidateQueries({ queryKey: queryKeys.systemConfigsEffective }),
queryClient.invalidateQueries({ queryKey: ['system-configs', 'history'] }),
]);
},
});
}