export const DINGTALK_APPROVAL_NO_MAX_LENGTH = 64 function isLeapYear(year: number): boolean { return year % 400 === 0 || (year % 4 === 0 && year % 100 !== 0) } function getDaysInMonth(year: number, month: number): number { if (month === 2) { return isLeapYear(year) ? 29 : 28 } return [4, 6, 9, 11].includes(month) ? 30 : 31 } function hasValidDatePrefix(value: string): boolean { const year = Number.parseInt(value.slice(0, 4), 10) const month = Number.parseInt(value.slice(4, 6), 10) const day = Number.parseInt(value.slice(6, 8), 10) if (year < 1000 || year > 9999 || month < 1 || month > 12 || day < 1) { return false } return day <= getDaysInMonth(year, month) } export function normalizeDingtalkApprovalNo( value: string | null | undefined, ): string | null { if (typeof value !== 'string') { return null } const normalized = value.trim() return normalized || null } export function getDingtalkApprovalNoError( value: string | null | undefined, ): string | null { const normalized = normalizeDingtalkApprovalNo(value) if (!normalized) { return null } if (!/^\d+$/.test(normalized)) { return '钉钉审批编号只能输入数字' } if (normalized.length < 9) { return '钉钉审批编号需包含8位日期和后续编号' } if (normalized.length > DINGTALK_APPROVAL_NO_MAX_LENGTH) { return `钉钉审批编号不能超过${DINGTALK_APPROVAL_NO_MAX_LENGTH}位` } if (!hasValidDatePrefix(normalized)) { return '钉钉审批编号前8位不是有效日期' } return null } export function validateDingtalkApprovalNo( value: string | null | undefined, ): boolean { return getDingtalkApprovalNoError(value) === null }