import { z, ZodTypeAny, ZodString, ZodNumber, ZodBoolean, ZodEffects, } from 'zod'; import { RegisterInfo } from '@/API/patient/workActions'; import { Rule } from 'antd/es/form'; import { PregnancyStatus } from '@/domain/patient/pregnancyStatus'; type IgnoredKeys = 'views' | 'study_type' | 'modality' | 'patient_type'; // 这些字段不在表单中填写 const registerInfoSchema: Record< keyof Omit, z.ZodTypeAny > = { accession_number: z.string().nonempty().regex(/^[a-zA-Z0-9_-]+$/, '只能输入数字、英文字符、下划线和连字符'), patient_name: z.string().nonempty(), patient_id: z.string().nonempty().regex(/^[a-zA-Z0-9_-]+$/, '只能输入数字、英文字符、下划线和连字符'), patient_size: z.string().nonempty(), patient_age: z.object({ number: z.number(), // 必须是数字 unit: z.string(), // 必须是字符串(根据你的需求,也可以是 z.enum(...) 等) }), patient_dob: z.string().nonempty(), patient_sex: z.string().optional(), ref_physician: z.string().optional(), operator_id: z.string().regex(/^[a-zA-Z0-9_-]*$/, '只能输入数字、英文字符、下划线和连字符').optional(), weight: z.number().optional(), thickness: z.number().optional(), length: z.number().optional(), comment: z.string().optional(), // views: z.array(z.object({ // view_id: z.string(), // procedure_id: z.string() // })).optional() /**---下面是宠物专用字段--- */ sex_neutered: z.string().optional(), chip_number: z.string().optional(), variety: z.string().optional(), owner_name: z.string().nonempty(), is_anaesthesia: z.boolean().optional(), is_sedation: z.boolean().optional(), /**---下面是人医专用字段--- */ pregnancy_status: z.nativeEnum(PregnancyStatus).optional(), }; const registerformSchema = z.object(registerInfoSchema); // 统一管理规则。规则表。描述要验证的字段【字段来源于被验证的数据结构UserInfo】,以及每个字段的"是否必填、必填标记、验证规则、提示信息、验证时机" export const registerFormFields = { accession_number: { label: 'Accession Number', required: true, requiredLabel: '', validation: zodToAntdRules(registerformSchema.shape.accession_number), message: 'Accession Number is optional', trigger: ['onChange', 'onBlur'], }, patient_id: { label: 'Patient ID', required: true, requiredLabel: 'Patient ID is required', validation: zodToAntdRules(registerformSchema.shape.patient_id), message: 'Patient ID is required', trigger: ['onChange', 'onBlur'], }, patient_name: { label: 'Patient Name', required: true, requiredLabel: 'Patient Name is required', validation: zodToAntdRules(registerformSchema.shape.patient_name), message: 'Patient Name is required', trigger: ['onChange', 'onBlur'], }, patient_size: { label: 'Patient Size', required: true, requiredLabel: 'Patient Size is required', validation: zodToAntdRules(registerformSchema.shape.patient_size), message: 'Patient Size is required', trigger: ['onChange', 'onBlur'], }, patient_age: { label: 'Patient Age', required: true, requiredLabel: 'Patient Age is required', validation: [ ...zodToAntdRules(registerformSchema.shape.patient_age), { validator: (_, value) => { if (!value || typeof value.number !== 'number') { return Promise.reject(new Error('请输入有效的年龄')); } if (value.number < 0) { return Promise.reject(new Error('年龄不能为负数')); } if (value.number > 200) { return Promise.reject(new Error('年龄超出合理范围(最大200)')); } if (!Number.isInteger(value.number)) { return Promise.reject(new Error('年龄必须为整数')); } return Promise.resolve(); }, }, ], message: 'Patient Age is required', trigger: ['onChange', 'onBlur'], }, patient_dob: { label: 'Patient DOB', required: true, requiredLabel: 'Patient DOB is required', validation: zodToAntdRules(registerformSchema.shape.patient_dob), message: 'Patient DOB is required', trigger: ['onChange', 'onBlur'], }, patient_sex: { label: 'Patient Sex', required: false, requiredLabel: 'Patient Sex is required', validation: zodToAntdRules(registerformSchema.shape.patient_sex), message: 'Patient Sex is required', trigger: ['onChange', 'onBlur'], }, // sex_neutered: { // label: 'Sex Neutered', // required: false, // requiredLabel: '', // validation: zodToAntdRules(registerformSchema.shape.sex_neutered), // message: 'Sex Neutered is optional', // trigger: ['onChange', 'onBlur'], // }, pregnancy_status: { label: 'Pregnancy Status', required: false, requiredLabel: '', validation: zodToAntdRules(registerformSchema.shape.pregnancy_status), message: 'Pregnancy Status is optional', trigger: ['onChange', 'onBlur'], }, // chip_number: { // label: 'Chip Number', // required: false, // requiredLabel: '', // validation: zodToAntdRules(registerformSchema.shape.chip_number), // message: 'Chip Number is optional', // trigger: ['onChange', 'onBlur'], // }, // variety: { // label: 'Variety', // required: false, // requiredLabel: '', // validation: zodToAntdRules(registerformSchema.shape.variety), // message: 'Variety is optional', // trigger: ['onChange', 'onBlur'], // }, // patient_type: { // label: 'Patient Type', // required: true, // requiredLabel: '', // validation: zodToAntdRules(registerformSchema.shape.patient_type), // message: 'Patient Type is optional', // trigger: ['onChange', 'onBlur'], // }, ref_physician: { label: 'Referring Physician', required: false, requiredLabel: '', validation: zodToAntdRules(registerformSchema.shape.ref_physician), message: 'Referring Physician is optional', trigger: ['onChange', 'onBlur'], }, operator_id: { label: 'Operator ID', required: false, requiredLabel: '', validation: zodToAntdRules(registerformSchema.shape.operator_id), message: 'Operator ID is optional', trigger: ['onChange', 'onBlur'], }, weight: { label: 'Weight', required: false, requiredLabel: '', validation: zodToAntdRules(registerformSchema.shape.weight), message: 'Weight is optional', trigger: ['onChange', 'onBlur'], }, thickness: { label: 'Thickness', required: false, requiredLabel: '', validation: zodToAntdRules(registerformSchema.shape.thickness), message: 'Thickness is optional', trigger: ['onChange', 'onBlur'], }, length: { label: 'Length', required: false, requiredLabel: '', validation: zodToAntdRules(registerformSchema.shape.length), message: 'Length is optional', trigger: ['onChange', 'onBlur'], }, comment: { label: 'Comment', required: false, requiredLabel: '', validation: zodToAntdRules(registerformSchema.shape.comment), message: 'Comment is optional', trigger: ['onChange', 'onBlur'], }, //动物专用字段 owner_name: { label: 'Owner Name', required: true, requiredLabel: '', validation: zodToAntdRules(registerformSchema.shape.owner_name), message: 'Owner Name is required', trigger: ['onChange', 'onBlur'], }, sex_neutered: { label: 'neutered', required: false, requiredLabel: '', validation: zodToAntdRules(registerformSchema.shape.sex_neutered), message: 'neutered is optional', trigger: ['onChange', 'onBlur'], }, chip_number: { label: 'Chip Number', required: false, requiredLabel: '', validation: zodToAntdRules(registerformSchema.shape.chip_number), message: 'Chip Number is optional', trigger: ['onChange', 'onBlur'], }, variety: { label: 'Variety', required: false, requiredLabel: '', validation: zodToAntdRules(registerformSchema.shape.variety), message: 'Variety is optional', trigger: ['onChange', 'onBlur'], }, is_anaesthesia: { label: 'Anaesthesia', required: false, requiredLabel: '', validation: zodToAntdRules(registerformSchema.shape.is_anaesthesia), message: 'Anaesthesia is optional', trigger: ['onChange', 'onBlur'], }, is_sedation: { label: 'Sedation', required: false, requiredLabel: '', validation: zodToAntdRules(registerformSchema.shape.is_sedation), message: 'Sedation is optional', trigger: ['onChange', 'onBlur'], }, } satisfies Record< keyof Omit, { label: string; required: boolean; requiredLabel: string; validation: Rule[]; message: string; trigger: ('onChange' | 'onBlur' | 'onSubmit')[]; } >; export default registerformSchema; type AntdRule = Rule & {}; export function zodToAntdRules(field: ZodTypeAny): AntdRule[] { const rules: AntdRule[] = []; // 解开 effects,比如 .refine() 或 .transform() while (field instanceof ZodEffects) { field = field._def.schema; } if (field instanceof ZodString) { for (const check of field._def.checks) { switch (check.kind) { case 'min': if (check.value === 1) { rules.push({ required: true, message: check.message || '必填项' }); } else { rules.push({ min: check.value, message: check.message ?? '' }); } break; case 'max': rules.push({ max: check.value, message: check.message ?? '' }); break; case 'email': rules.push({ type: 'email', message: check.message ?? '' }); break; case 'regex': rules.push({ pattern: check.regex, message: check.message ?? '' }); break; case 'length': rules.push({ len: check.value, message: check.message ?? '' }); break; } } } if (field instanceof ZodNumber) { for (const check of field._def.checks) { switch (check.kind) { case 'min': rules.push({ min: check.value, message: check.message ?? '' }); break; case 'max': rules.push({ max: check.value, message: check.message ?? '' }); break; case 'int': rules.push({ type: 'integer', message: check.message || '必须为整数', }); break; } } } if (field instanceof ZodBoolean) { rules.push({ type: 'boolean', message: '必须为布尔值' }); } return rules; }