Преглед на файлове

feat(critical-values): 添加危急值相关接口及页面逻辑,支持数据加载、保存和重置功能

mcbaiyun преди 2 седмици
родител
ревизия
157ebfc9f3
променени са 2 файла, в които са добавени 157 реда и са изтрити 27 реда
  1. 30 0
      src/api/criticalValues.ts
  2. 127 27
      src/pages/doctor/manage/critical-values.vue

+ 30 - 0
src/api/criticalValues.ts

@@ -0,0 +1,30 @@
+// 危急值(整表)相关接口
+import request from './request'
+
+export async function getCriticalValuesForm() {
+  const res: any = await request({
+    url: 'https://wx.baiyun.work/critical-values/form',
+    method: 'GET'
+  })
+  return res
+}
+
+export async function saveCriticalValuesForm(payload: any) {
+  const res: any = await request({
+    url: 'https://wx.baiyun.work/critical-values/form',
+    method: 'POST',
+    header: {
+      'content-type': 'application/json'
+    },
+    data: payload
+  })
+  return res
+}
+
+export async function resetCriticalValuesForm() {
+  const res: any = await request({
+    url: 'https://wx.baiyun.work/critical-values/form',
+    method: 'DELETE'
+  })
+  return res
+}

+ 127 - 27
src/pages/doctor/manage/critical-values.vue

@@ -8,10 +8,10 @@
           <text class="label">身高(cm)</text>
           <text class="label">身高(cm)</text>
           <view class="inputs">
           <view class="inputs">
             <view class="input-group">
             <view class="input-group">
-              <input type="number" class="num-input" v-model.number="form.physical.heightMin" placeholder="下限 例如 140" />
+              <input type="number" class="num-input" v-model="form.physical.heightMin" placeholder="下限 例如 140" />
             </view>
             </view>
             <view class="input-group">
             <view class="input-group">
-              <input type="number" class="num-input" v-model.number="form.physical.heightMax" placeholder="上限 例如 200" />
+              <input type="number" class="num-input" v-model="form.physical.heightMax" placeholder="上限 例如 200" />
             </view>
             </view>
           </view>
           </view>
         </view>
         </view>
@@ -20,10 +20,10 @@
           <text class="label">体重(kg)</text>
           <text class="label">体重(kg)</text>
           <view class="inputs">
           <view class="inputs">
             <view class="input-group">
             <view class="input-group">
-              <input type="number" class="num-input" v-model.number="form.physical.weightMin" placeholder="下限 例如 40" />
+              <input type="number" class="num-input" v-model="form.physical.weightMin" placeholder="下限 例如 40" />
             </view>
             </view>
             <view class="input-group">
             <view class="input-group">
-              <input type="number" class="num-input" v-model.number="form.physical.weightMax" placeholder="上限 例如 100" />
+              <input type="number" class="num-input" v-model="form.physical.weightMax" placeholder="上限 例如 100" />
             </view>
             </view>
           </view>
           </view>
         </view>
         </view>
@@ -32,10 +32,10 @@
           <text class="label">BMI</text>
           <text class="label">BMI</text>
           <view class="inputs">
           <view class="inputs">
             <view class="input-group">
             <view class="input-group">
-              <input type="number" class="num-input" v-model.number="form.bmi.min" placeholder="下限 例如 18.5" />
+              <input type="number" class="num-input" v-model="form.bmi.min" placeholder="下限 例如 18.5" />
             </view>
             </view>
             <view class="input-group">
             <view class="input-group">
-              <input type="number" class="num-input" v-model.number="form.bmi.max" placeholder="上限 例如 24" />
+              <input type="number" class="num-input" v-model="form.bmi.max" placeholder="上限 例如 24" />
             </view>
             </view>
           </view>
           </view>
         </view>
         </view>
@@ -49,10 +49,10 @@
           <text class="label">收缩压(高压)</text>
           <text class="label">收缩压(高压)</text>
           <view class="inputs">
           <view class="inputs">
             <view class="input-group">
             <view class="input-group">
-              <input type="number" class="num-input" v-model.number="form.bloodPressure.systolicMin" placeholder="下限 例如 90" />
+              <input type="number" class="num-input" v-model="form.bloodPressure.systolicMin" placeholder="下限 例如 90" />
             </view>
             </view>
             <view class="input-group">
             <view class="input-group">
-              <input type="number" class="num-input" v-model.number="form.bloodPressure.systolicMax" placeholder="上限 例如 140" />
+              <input type="number" class="num-input" v-model="form.bloodPressure.systolicMax" placeholder="上限 例如 140" />
             </view>
             </view>
           </view>
           </view>
         </view>
         </view>
@@ -61,10 +61,10 @@
           <text class="label">舒张压(低压)</text>
           <text class="label">舒张压(低压)</text>
           <view class="inputs">
           <view class="inputs">
             <view class="input-group">
             <view class="input-group">
-              <input type="number" class="num-input" v-model.number="form.bloodPressure.diastolicMin" placeholder="下限 例如 60" />
+              <input type="number" class="num-input" v-model="form.bloodPressure.diastolicMin" placeholder="下限 例如 60" />
             </view>
             </view>
             <view class="input-group">
             <view class="input-group">
-              <input type="number" class="num-input" v-model.number="form.bloodPressure.diastolicMax" placeholder="上限 例如 90" />
+              <input type="number" class="num-input" v-model="form.bloodPressure.diastolicMax" placeholder="上限 例如 90" />
             </view>
             </view>
           </view>
           </view>
         </view>
         </view>
@@ -78,10 +78,10 @@
           <text class="label">空腹</text>
           <text class="label">空腹</text>
           <view class="inputs">
           <view class="inputs">
             <view class="input-group">
             <view class="input-group">
-              <input type="number" class="num-input" step="0.1" v-model.number="form.bloodGlucose.fastingMin" placeholder="下限 例如 3.9" />
+              <input type="number" class="num-input" step="0.1" v-model="form.bloodGlucose.fastingMin" placeholder="下限 例如 3.9" />
             </view>
             </view>
             <view class="input-group">
             <view class="input-group">
-              <input type="number" class="num-input" step="0.1" v-model.number="form.bloodGlucose.fastingMax" placeholder="上限 例如 7.0" />
+              <input type="number" class="num-input" step="0.1" v-model="form.bloodGlucose.fastingMax" placeholder="上限 例如 7.0" />
             </view>
             </view>
           </view>
           </view>
         </view>
         </view>
@@ -90,10 +90,10 @@
           <text class="label">随机</text>
           <text class="label">随机</text>
           <view class="inputs">
           <view class="inputs">
             <view class="input-group">
             <view class="input-group">
-              <input type="number" class="num-input" step="0.1" v-model.number="form.bloodGlucose.randomMin" placeholder="下限 例如 4.4" />
+              <input type="number" class="num-input" step="0.1" v-model="form.bloodGlucose.randomMin" placeholder="下限 例如 4.4" />
             </view>
             </view>
             <view class="input-group">
             <view class="input-group">
-              <input type="number" class="num-input" step="0.1" v-model.number="form.bloodGlucose.randomMax" placeholder="上限 例如 11.1" />
+              <input type="number" class="num-input" step="0.1" v-model="form.bloodGlucose.randomMax" placeholder="上限 例如 11.1" />
             </view>
             </view>
           </view>
           </view>
         </view>
         </view>
@@ -107,10 +107,10 @@
           <text class="label">心率</text>
           <text class="label">心率</text>
           <view class="inputs">
           <view class="inputs">
             <view class="input-group">
             <view class="input-group">
-              <input type="number" class="num-input" v-model.number="form.heartRate.min" placeholder="下限 例如 50" />
+              <input type="number" class="num-input" v-model="form.heartRate.min" placeholder="下限 例如 50" />
             </view>
             </view>
             <view class="input-group">
             <view class="input-group">
-              <input type="number" class="num-input" v-model.number="form.heartRate.max" placeholder="上限 例如 120" />
+              <input type="number" class="num-input" v-model="form.heartRate.max" placeholder="上限 例如 120" />
             </view>
             </view>
           </view>
           </view>
         </view>
         </view>
@@ -132,6 +132,7 @@
 import { ref, onMounted } from 'vue'
 import { ref, onMounted } from 'vue'
 import CustomNav from '@/components/custom-nav.vue'
 import CustomNav from '@/components/custom-nav.vue'
 import TabBar from '@/components/tab-bar.vue'
 import TabBar from '@/components/tab-bar.vue'
+import { getCriticalValuesForm, saveCriticalValuesForm, resetCriticalValuesForm } from '@/api/criticalValues'
 
 
 interface PhysicalCritical {
 interface PhysicalCritical {
   heightMin: number | null
   heightMin: number | null
@@ -174,8 +175,30 @@ const form = ref<CriticalValues>({
   heartRate: { min: null, max: null }
   heartRate: { min: null, max: null }
 })
 })
 
 
-onMounted(() => {
-  // 优先读取新键
+onMounted(async () => {
+  // 尝试从后端加载整表配置,若失败再回退到本地缓存
+  try {
+    const res: any = await getCriticalValuesForm()
+    if (res.statusCode === 401) {
+      uni.removeStorageSync('token')
+      uni.removeStorageSync('role')
+      uni.reLaunch({ url: '/pages/public/login/index' })
+      return
+    }
+    if ((res.data as any) && (res.data as any).code === 200) {
+      const payload = (res.data as any).data
+      if (payload) {
+        form.value = { ...form.value, ...payload }
+        // 更新本地缓存以便离线使用
+        try { uni.setStorageSync(STORAGE_KEY, JSON.stringify(form.value)) } catch (e) { /* ignore */ }
+        return
+      }
+    }
+  } catch (e) {
+    // 网络或其他异常,回退到本地缓存
+  }
+
+  // 回退:优先读取新键
   const savedNew: any = uni.getStorageSync(STORAGE_KEY)
   const savedNew: any = uni.getStorageSync(STORAGE_KEY)
   if (savedNew) {
   if (savedNew) {
     try {
     try {
@@ -206,10 +229,20 @@ onMounted(() => {
   }
   }
 })
 })
 
 
-const validatePairs = (min: number | null, max: number | null, label: string) => {
-  if (min != null && max != null && min > max) {
-    return `${label}:下限不能大于上限`
+const validatePairs = (min: any, max: any, label: string) => {
+  const isEmpty = (v: any) => v === null || v === undefined || v === ''
+  if (isEmpty(min) || isEmpty(max)) return '' // 只在两边都有值时才校验
+
+  const toNumber = (v: any) => {
+    if (isEmpty(v)) return null
+    const n = Number(v)
+    return Number.isNaN(n) ? null : n
   }
   }
+
+  const minN = toNumber(min)
+  const maxN = toNumber(max)
+  if (minN == null || maxN == null) return ''
+  if (minN > maxN) return `${label}:下限不能大于上限`
   return ''
   return ''
 }
 }
 
 
@@ -229,23 +262,90 @@ const validateForm = (): { ok: boolean; message?: string } => {
   return { ok: true }
   return { ok: true }
 }
 }
 
 
-const save = () => {
+const save = async () => {
   const v = validateForm()
   const v = validateForm()
   if (!v.ok) {
   if (!v.ok) {
     uni.showToast({ title: v.message || '校验失败', icon: 'none' })
     uni.showToast({ title: v.message || '校验失败', icon: 'none' })
     return
     return
   }
   }
+  // 规范化表单:将空字符串/undefined 转为 null,将非空字符串转为 Number
+  const normalize = (v: any) => {
+    if (v === null || v === undefined || v === '') return null
+    const n = Number(v)
+    return Number.isNaN(n) ? null : n
+  }
+
+  const payload = {
+    physical: {
+      heightMin: normalize(form.value.physical.heightMin),
+      heightMax: normalize(form.value.physical.heightMax),
+      weightMin: normalize(form.value.physical.weightMin),
+      weightMax: normalize(form.value.physical.weightMax)
+    },
+    bmi: {
+      min: normalize(form.value.bmi.min),
+      max: normalize(form.value.bmi.max)
+    },
+    bloodPressure: {
+      systolicMin: normalize(form.value.bloodPressure.systolicMin),
+      systolicMax: normalize(form.value.bloodPressure.systolicMax),
+      diastolicMin: normalize(form.value.bloodPressure.diastolicMin),
+      diastolicMax: normalize(form.value.bloodPressure.diastolicMax)
+    },
+    bloodGlucose: {
+      fastingMin: normalize(form.value.bloodGlucose.fastingMin),
+      fastingMax: normalize(form.value.bloodGlucose.fastingMax),
+      randomMin: normalize(form.value.bloodGlucose.randomMin),
+      randomMax: normalize(form.value.bloodGlucose.randomMax)
+    },
+    heartRate: {
+      min: normalize(form.value.heartRate.min),
+      max: normalize(form.value.heartRate.max)
+    }
+  }
 
 
   try {
   try {
-    uni.setStorageSync(STORAGE_KEY, JSON.stringify(form.value))
-    uni.showToast({ title: '保存成功', icon: 'success' })
-  } catch (e) {
+    // 先尝试调用后端接口保存(使用规范化后的 payload)
+    const res: any = await saveCriticalValuesForm(payload)
+    if ((res.data as any) && (res.data as any).code === 200) {
+      // 更新本地缓存
+      try { uni.setStorageSync(STORAGE_KEY, JSON.stringify(form.value)) } catch (e) { /* ignore */ }
+      uni.showToast({ title: '保存成功', icon: 'success' })
+      return
+    }
     uni.showToast({ title: '保存失败', icon: 'none' })
     uni.showToast({ title: '保存失败', icon: 'none' })
+  } catch (e) {
+    // 网络或其它错误,回退到本地缓存保存
+    try {
+      uni.setStorageSync(STORAGE_KEY, JSON.stringify(form.value))
+      uni.showToast({ title: '已保存到本地(离线)', icon: 'success' })
+    } catch (err) {
+      uni.showToast({ title: '保存失败', icon: 'none' })
+    }
   }
   }
 }
 }
 
 
-const resetForm = () => {
-  form.value = { 
+const resetForm = async () => {
+  try {
+    const res: any = await resetCriticalValuesForm()
+    if ((res.data as any) && (res.data as any).code === 200) {
+      form.value = {
+        physical: { heightMin: null, heightMax: null, weightMin: null, weightMax: null },
+        bmi: { min: null, max: null },
+        bloodPressure: { systolicMin: null, systolicMax: null, diastolicMin: null, diastolicMax: null },
+        bloodGlucose: { fastingMin: null, fastingMax: null, randomMin: null, randomMax: null },
+        heartRate: { min: null, max: null }
+      }
+      try { uni.removeStorageSync(STORAGE_KEY) } catch (e) { /* ignore */ }
+      uni.showToast({ title: '已重置', icon: 'success' })
+      return
+    }
+  } catch (e) {
+    // ignore and fallthrough to local reset
+  }
+
+  // 回退:本地清理
+  form.value = {
     physical: { heightMin: null, heightMax: null, weightMin: null, weightMax: null },
     physical: { heightMin: null, heightMax: null, weightMin: null, weightMax: null },
     bmi: { min: null, max: null },
     bmi: { min: null, max: null },
     bloodPressure: { systolicMin: null, systolicMax: null, diastolicMin: null, diastolicMax: null },
     bloodPressure: { systolicMin: null, systolicMax: null, diastolicMin: null, diastolicMax: null },