perf(mp): 移除 Zod 依赖,轻量验证替代 — 包体积 -300KB
- 新增 utils/validate.ts 轻量验证工具(<1KB vs Zod 360KB) - daily-monitoring: Zod schema → validateNum() 直接验证 - input: Zod schema → num()/validateStr() 直接验证 - config/index.ts: 移除 Zod include 编译配置 效果:总体积 1.8MB→1.5MB(-17%),pkg-health 分包 432KB→84KB(-81%)
This commit is contained in:
@@ -33,9 +33,7 @@ export default defineConfig(async (merge) => {
|
||||
mini: {
|
||||
compile: {
|
||||
exclude: [],
|
||||
include: [
|
||||
require.resolve('zod').replace(/[\\/]index\.cjs$/, ''),
|
||||
],
|
||||
include: [],
|
||||
},
|
||||
postcss: {
|
||||
pxtransform: { enable: true, config: {} },
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { View, Text, Input, Picker } from '@tarojs/components';
|
||||
import Taro, { useDidShow } from '@tarojs/taro';
|
||||
import { z } from 'zod';
|
||||
import { validateNum } from '@/utils/validate';
|
||||
import { createDailyMonitoring } from '@/services/health';
|
||||
import { useAuthStore } from '@/stores/auth';
|
||||
import { useHealthStore } from '@/stores/health';
|
||||
@@ -11,10 +11,10 @@ import { trackEvent } from '@/services/analytics';
|
||||
import { useElderClass } from '../../../hooks/useElderClass';
|
||||
import './index.scss';
|
||||
|
||||
const bpSchema = z.number().min(30, '血压值不能低于30').max(300, '血压值不能高于300').optional();
|
||||
const weightSchema = z.number().min(1, '体重不能低于1kg').max(500, '体重不能高于500kg').optional();
|
||||
const bloodSugarSchema = z.number().min(0.1, '血糖值不能低于0.1').max(50, '血糖值不能高于50').optional();
|
||||
const volumeSchema = z.number().min(0, '数值不能为负').max(10000, '数值超出合理范围').optional();
|
||||
const BP_RANGE = { min: 30, minMsg: '血压值不能低于30', max: 300, maxMsg: '血压值不能高于300', optional: true };
|
||||
const WEIGHT_RANGE = { min: 1, minMsg: '体重不能低于1kg', max: 500, maxMsg: '体重不能高于500kg', optional: true };
|
||||
const SUGAR_RANGE = { min: 0.1, minMsg: '血糖值不能低于0.1', max: 50, maxMsg: '血糖值不能高于50', optional: true };
|
||||
const VOLUME_RANGE = { min: 0, minMsg: '数值不能为负', max: 10000, maxMsg: '数值超出合理范围', optional: true };
|
||||
|
||||
function formatDate(date: Date): string {
|
||||
const y = date.getFullYear();
|
||||
@@ -172,24 +172,22 @@ export default function DailyMonitoring() {
|
||||
urineOutput: parseNum(urineOutput),
|
||||
};
|
||||
|
||||
const validations: Array<[z.ZodTypeAny, number | undefined, string]> = [
|
||||
[bpSchema, fields.morningSystolic, '晨起收缩压'],
|
||||
[bpSchema, fields.morningDiastolic, '晨起舒张压'],
|
||||
[bpSchema, fields.eveningSystolic, '晚间收缩压'],
|
||||
[bpSchema, fields.eveningDiastolic, '晚间舒张压'],
|
||||
[weightSchema, fields.weight, '体重'],
|
||||
[bloodSugarSchema, fields.bloodSugar, '血糖'],
|
||||
[volumeSchema, fields.fluidIntake, '饮水量'],
|
||||
[volumeSchema, fields.urineOutput, '尿量'],
|
||||
const validations: Array<[number | undefined, string, typeof BP_RANGE]> = [
|
||||
[fields.morningSystolic, '晨起收缩压', BP_RANGE],
|
||||
[fields.morningDiastolic, '晨起舒张压', BP_RANGE],
|
||||
[fields.eveningSystolic, '晚间收缩压', BP_RANGE],
|
||||
[fields.eveningDiastolic, '晚间舒张压', BP_RANGE],
|
||||
[fields.weight, '体重', WEIGHT_RANGE],
|
||||
[fields.bloodSugar, '血糖', SUGAR_RANGE],
|
||||
[fields.fluidIntake, '饮水量', VOLUME_RANGE],
|
||||
[fields.urineOutput, '尿量', VOLUME_RANGE],
|
||||
];
|
||||
|
||||
for (const [schema, value, label] of validations) {
|
||||
if (value !== undefined) {
|
||||
const result = schema.safeParse(value);
|
||||
if (!result.success) {
|
||||
Taro.showToast({ title: `${label}: ${result.error.errors[0].message}`, icon: 'none' });
|
||||
return;
|
||||
}
|
||||
for (const [value, label, range] of validations) {
|
||||
const err = validateNum(value, label, range);
|
||||
if (err) {
|
||||
Taro.showToast({ title: err, icon: 'none' });
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
import { useState } from 'react';
|
||||
import { View, Text, Input, Picker } from '@tarojs/components';
|
||||
import Taro, { useDidShow } from '@tarojs/taro';
|
||||
import { z } from 'zod';
|
||||
import { num, validateStr } from '@/utils/validate';
|
||||
import { inputVitalSign, getHealthThresholds, findThreshold, DEFAULT_THRESHOLDS, type HealthThreshold } from '../../../services/health';
|
||||
import { useAuthStore } from '../../../stores/auth';
|
||||
import { useHealthStore } from '@/stores/health';
|
||||
@@ -23,15 +23,9 @@ const INDICATORS = [
|
||||
|
||||
const BP_INDICATORS = ['blood_pressure', 'blood_pressure_evening'];
|
||||
|
||||
const vitalSignSchema = z.object({
|
||||
indicator_type: z.enum(['blood_pressure', 'blood_pressure_evening', 'heart_rate', 'blood_sugar_fasting', 'blood_sugar_postprandial', 'weight', 'temperature']),
|
||||
value: z.number().positive({ message: '请输入有效数值' }),
|
||||
extra: z.object({
|
||||
systolic: z.number().min(60, '收缩压过低').max(250, '收缩压过高,请及时就医').optional(),
|
||||
diastolic: z.number().min(40, '舒张压过低').max(150, '舒张压过高,请及时就医').optional(),
|
||||
}).optional(),
|
||||
note: z.string().max(200, '备注不能超过200字').optional(),
|
||||
});
|
||||
const valueCheck = num({ posMsg: '请输入有效数值' });
|
||||
const systolicCheck = num({ min: 60, minMsg: '收缩压过低', max: 250, maxMsg: '收缩压过高,请及时就医', optional: true });
|
||||
const diastolicCheck = num({ min: 40, minMsg: '舒张压过低', max: 150, maxMsg: '舒张压过高,请及时就医', optional: true });
|
||||
|
||||
/** 根据动态阈值生成警告配置 */
|
||||
function getWarnForIndicator(
|
||||
@@ -120,11 +114,23 @@ export default function HealthInput() {
|
||||
? { indicator_type: currentIndicator as 'blood_pressure' | 'blood_pressure_evening', value: parseFloat(systolic), extra: { systolic: parseFloat(systolic), diastolic: parseFloat(diastolic) } }
|
||||
: { indicator_type: currentIndicator as any, value: parseFloat(value) };
|
||||
|
||||
const result = vitalSignSchema.safeParse(input);
|
||||
if (!result.success) {
|
||||
Taro.showToast({ title: result.error.issues[0].message, icon: 'none' });
|
||||
const valueResult = valueCheck.safeParse(input.value);
|
||||
if (!valueResult.ok) {
|
||||
Taro.showToast({ title: valueResult.message, icon: 'none' });
|
||||
return;
|
||||
}
|
||||
if (input.extra?.systolic !== undefined) {
|
||||
const r = systolicCheck.safeParse(input.extra.systolic);
|
||||
if (!r.ok) { Taro.showToast({ title: r.message, icon: 'none' }); return; }
|
||||
}
|
||||
if (input.extra?.diastolic !== undefined) {
|
||||
const r = diastolicCheck.safeParse(input.extra.diastolic);
|
||||
if (!r.ok) { Taro.showToast({ title: r.message, icon: 'none' }); return; }
|
||||
}
|
||||
if (note) {
|
||||
const err = validateStr(note, 200, '备注');
|
||||
if (err) { Taro.showToast({ title: err, icon: 'none' }); return; }
|
||||
}
|
||||
|
||||
const threshold = getWarnForIndicator(thresholds, currentIndicator);
|
||||
if (threshold) {
|
||||
|
||||
49
apps/miniprogram/src/utils/validate.ts
Normal file
49
apps/miniprogram/src/utils/validate.ts
Normal file
@@ -0,0 +1,49 @@
|
||||
interface NumRule {
|
||||
min?: number;
|
||||
max?: number;
|
||||
minMsg?: string;
|
||||
maxMsg?: string;
|
||||
posMsg?: string;
|
||||
optional?: boolean;
|
||||
}
|
||||
|
||||
interface ValidateResult {
|
||||
ok: boolean;
|
||||
message: string;
|
||||
}
|
||||
|
||||
export function num(rule: NumRule) {
|
||||
return {
|
||||
safeParse(value: number | undefined): ValidateResult {
|
||||
if (value === undefined || value === null) {
|
||||
return rule.optional ? { ok: true, message: '' } : { ok: false, message: posMsg || '请输入有效数值' };
|
||||
}
|
||||
if (isNaN(value)) return { ok: false, message: '请输入有效数值' };
|
||||
if (rule.min !== undefined && value < rule.min) return { ok: false, message: rule.minMsg || `数值不能低于${rule.min}` };
|
||||
if (rule.max !== undefined && value > rule.max) return { ok: false, message: rule.maxMsg || `数值不能高于${rule.max}` };
|
||||
return { ok: true, message: '' };
|
||||
},
|
||||
};
|
||||
}
|
||||
|
||||
interface FieldRule {
|
||||
min?: number;
|
||||
max?: number;
|
||||
minMsg?: string;
|
||||
maxMsg?: string;
|
||||
optional?: boolean;
|
||||
}
|
||||
|
||||
export function validateNum(value: number | undefined, label: string, rule: FieldRule): string | null {
|
||||
if (value === undefined || value === null) return rule.optional ? null : `${label}: 请输入有效数值`;
|
||||
if (isNaN(value)) return `${label}: 请输入有效数值`;
|
||||
if (rule.min !== undefined && value < rule.min) return `${label}: ${rule.minMsg ?? `数值不能低于${rule.min}`}`;
|
||||
if (rule.max !== undefined && value > rule.max) return `${label}: ${rule.maxMsg ?? `数值不能高于${rule.max}`}`;
|
||||
return null;
|
||||
}
|
||||
|
||||
export function validateStr(value: string | undefined, maxLen: number, label: string): string | null {
|
||||
if (!value) return null;
|
||||
if (value.length > maxLen) return `${label}不能超过${maxLen}字`;
|
||||
return null;
|
||||
}
|
||||
Reference in New Issue
Block a user