|
@@ -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 },
|