T40 UI 审计修复(60 页面全覆盖): - 新增 $acc-d/$wrn-d 渐变中间色变量,修复首页轮播渐变硬编码 - 替换 8 处裸 white 为 $white 设计变量(5 个 SCSS 文件) - 修复 7 处触摸目标 40/44px → 48px(健康/消息/咨询/预约/首页) - 3 页面新增 Loading 状态(体征录入/个人中心/就诊人添加) - statusTag 移除硬编码布局值,改用 SCSS mixin 控制 - 医生端 14 页面架构 Hook 层补充(useThrottledDidShow 替换 useEffect) - 移除 action-inbox 未使用 import 安全 P0 修复: - JWT 中间件加固:token 类型校验 + 过期预检 + 类型别名简化 - 速率限制增强:滑动窗口 + 暴力破解防护 - analytics handler 错误处理完善 文档: - T40 审计报告(24 PASS / 36 PASS_WITH_ISSUES / 0 NEEDS_WORK) - 5 份 DevTools/性能审计讨论记录 - wiki 症状导航 + 小程序章节更新
199 lines
7.5 KiB
TypeScript
199 lines
7.5 KiB
TypeScript
import { useState } from 'react';
|
|
import { View, Text, Input, Textarea, Picker, ScrollView } from '@tarojs/components';
|
|
import Taro, { useRouter } from '@tarojs/taro';
|
|
import { createDialysisPrescription } from '@/services/doctor/dialysis';
|
|
import Loading from '@/components/Loading';
|
|
import { useElderClass } from '../../../../hooks/useElderClass';
|
|
import './index.scss';
|
|
|
|
interface FormState {
|
|
dialyzer_model: string;
|
|
membrane_area: string;
|
|
dialysate_potassium: string;
|
|
dialysate_calcium: string;
|
|
dialysate_bicarbonate: string;
|
|
anticoagulation_type: string;
|
|
anticoagulation_dose: string;
|
|
target_ultrafiltration_ml: string;
|
|
target_dry_weight: string;
|
|
blood_flow_rate: string;
|
|
dialysate_flow_rate: string;
|
|
frequency_per_week: string;
|
|
duration_minutes: string;
|
|
vascular_access_type: string;
|
|
vascular_access_location: string;
|
|
effective_from: string;
|
|
effective_to: string;
|
|
notes: string;
|
|
}
|
|
|
|
const initialForm: FormState = {
|
|
dialyzer_model: '',
|
|
membrane_area: '',
|
|
dialysate_potassium: '',
|
|
dialysate_calcium: '',
|
|
dialysate_bicarbonate: '',
|
|
anticoagulation_type: '',
|
|
anticoagulation_dose: '',
|
|
target_ultrafiltration_ml: '',
|
|
target_dry_weight: '',
|
|
blood_flow_rate: '',
|
|
dialysate_flow_rate: '',
|
|
frequency_per_week: '',
|
|
duration_minutes: '',
|
|
vascular_access_type: '',
|
|
vascular_access_location: '',
|
|
effective_from: '',
|
|
effective_to: '',
|
|
notes: '',
|
|
};
|
|
|
|
export default function PrescriptionCreate() {
|
|
const router = useRouter();
|
|
const patientId = router.params.patientId || '';
|
|
const modeClass = useElderClass();
|
|
const [form, setForm] = useState<FormState>(initialForm);
|
|
const [submitting, setSubmitting] = useState(false);
|
|
|
|
const updateField = (key: keyof FormState, value: string) => {
|
|
setForm((prev) => ({ ...prev, [key]: value }));
|
|
};
|
|
|
|
const handleSubmit = async () => {
|
|
if (!patientId) {
|
|
Taro.showToast({ title: '缺少患者信息', icon: 'none' });
|
|
return;
|
|
}
|
|
|
|
setSubmitting(true);
|
|
const num = (v: string) => v ? Number(v) : undefined;
|
|
const payload = {
|
|
patient_id: patientId,
|
|
dialyzer_model: form.dialyzer_model || undefined,
|
|
membrane_area: num(form.membrane_area),
|
|
dialysate_potassium: num(form.dialysate_potassium),
|
|
dialysate_calcium: num(form.dialysate_calcium),
|
|
dialysate_bicarbonate: num(form.dialysate_bicarbonate),
|
|
anticoagulation_type: form.anticoagulation_type || undefined,
|
|
anticoagulation_dose: form.anticoagulation_dose || undefined,
|
|
target_ultrafiltration_ml: num(form.target_ultrafiltration_ml),
|
|
target_dry_weight: num(form.target_dry_weight),
|
|
blood_flow_rate: num(form.blood_flow_rate),
|
|
dialysate_flow_rate: num(form.dialysate_flow_rate),
|
|
frequency_per_week: num(form.frequency_per_week),
|
|
duration_minutes: num(form.duration_minutes),
|
|
vascular_access_type: form.vascular_access_type || undefined,
|
|
vascular_access_location: form.vascular_access_location || undefined,
|
|
effective_from: form.effective_from || undefined,
|
|
effective_to: form.effective_to || undefined,
|
|
notes: form.notes || undefined,
|
|
};
|
|
|
|
try {
|
|
await createDialysisPrescription(payload);
|
|
Taro.showToast({ title: '创建成功', icon: 'success' });
|
|
setTimeout(() => Taro.navigateBack(), 1000);
|
|
} catch {
|
|
Taro.showToast({ title: '创建失败', icon: 'none' });
|
|
} finally {
|
|
setSubmitting(false);
|
|
}
|
|
};
|
|
|
|
const InputField = ({ label, field, placeholder, type = 'digit' }: {
|
|
label: string; field: keyof FormState; placeholder: string; type?: string;
|
|
}) => (
|
|
<View className='form-row'>
|
|
<Text className='form-label'>{label}</Text>
|
|
<Input
|
|
className='form-input'
|
|
type={type as any}
|
|
placeholder={placeholder}
|
|
value={form[field]}
|
|
onInput={(e) => updateField(field, e.detail.value)}
|
|
/>
|
|
</View>
|
|
);
|
|
|
|
return (
|
|
<ScrollView scrollY className={`create-page ${modeClass}`}>
|
|
{/* 透析器 */}
|
|
<View className='section'>
|
|
<Text className='section-title'>透析器</Text>
|
|
<InputField label='透析器型号' field='dialyzer_model' placeholder='请输入型号' type='text' />
|
|
<InputField label='膜面积' field='membrane_area' placeholder='m²' />
|
|
</View>
|
|
|
|
{/* 透析液 */}
|
|
<View className='section'>
|
|
<Text className='section-title'>透析液配比</Text>
|
|
<InputField label='钾浓度' field='dialysate_potassium' placeholder='mmol/L' />
|
|
<InputField label='钙浓度' field='dialysate_calcium' placeholder='mmol/L' />
|
|
<InputField label='碳酸氢盐' field='dialysate_bicarbonate' placeholder='mmol/L' />
|
|
</View>
|
|
|
|
{/* 抗凝 */}
|
|
<View className='section'>
|
|
<Text className='section-title'>抗凝方案</Text>
|
|
<InputField label='抗凝类型' field='anticoagulation_type' placeholder='请输入' type='text' />
|
|
<InputField label='抗凝剂量' field='anticoagulation_dose' placeholder='请输入' type='text' />
|
|
</View>
|
|
|
|
{/* 参数 */}
|
|
<View className='section'>
|
|
<Text className='section-title'>参数设置</Text>
|
|
<InputField label='目标超滤量' field='target_ultrafiltration_ml' placeholder='ml' type='number' />
|
|
<InputField label='目标干体重' field='target_dry_weight' placeholder='kg' />
|
|
<InputField label='血流速' field='blood_flow_rate' placeholder='ml/min' type='number' />
|
|
<InputField label='透析液流量' field='dialysate_flow_rate' placeholder='ml/min' type='number' />
|
|
<InputField label='每周频次' field='frequency_per_week' placeholder='次/周' type='number' />
|
|
<InputField label='每次时长' field='duration_minutes' placeholder='分钟' type='number' />
|
|
</View>
|
|
|
|
{/* 血管通路 */}
|
|
<View className='section'>
|
|
<Text className='section-title'>血管通路</Text>
|
|
<InputField label='通路类型' field='vascular_access_type' placeholder='请输入' type='text' />
|
|
<InputField label='通路位置' field='vascular_access_location' placeholder='请输入' type='text' />
|
|
</View>
|
|
|
|
{/* 生效日期 */}
|
|
<View className='section'>
|
|
<Text className='section-title'>生效日期</Text>
|
|
<View className='form-row'>
|
|
<Text className='form-label'>生效日期</Text>
|
|
<Picker mode='date' value={form.effective_from} onChange={(e) => updateField('effective_from', e.detail.value)}>
|
|
<Text className={`form-value ${!form.effective_from ? 'placeholder' : ''}`}>
|
|
{form.effective_from || '请选择'}
|
|
</Text>
|
|
</Picker>
|
|
</View>
|
|
<View className='form-row'>
|
|
<Text className='form-label'>失效日期</Text>
|
|
<Picker mode='date' value={form.effective_to} onChange={(e) => updateField('effective_to', e.detail.value)}>
|
|
<Text className={`form-value ${!form.effective_to ? 'placeholder' : ''}`}>
|
|
{form.effective_to || '请选择'}
|
|
</Text>
|
|
</Picker>
|
|
</View>
|
|
</View>
|
|
|
|
{/* 备注 */}
|
|
<View className='section'>
|
|
<Text className='section-title'>备注</Text>
|
|
<Textarea
|
|
className='form-textarea'
|
|
placeholder='请输入备注...'
|
|
value={form.notes}
|
|
onInput={(e) => updateField('notes', e.detail.value)}
|
|
maxlength={500}
|
|
/>
|
|
</View>
|
|
|
|
<View className={`submit-btn ${submitting ? 'submit-btn--disabled' : ''}`} onClick={handleSubmit}>
|
|
<Text className='submit-btn__text'>{submitting ? '提交中...' : '创建处方'}</Text>
|
|
</View>
|
|
</ScrollView>
|
|
);
|
|
}
|