This commit is contained in:
Your Name
2026-05-28 14:41:50 +08:00
parent 33f8f669ad
commit 2b1ce61e72
4 changed files with 460 additions and 127 deletions
+255 -9
View File
@@ -82,8 +82,8 @@
</view>
<view class="heatmap-foot">
<text class="heatmap-foot-text">{{ todayChecked ? '今日已完成记录' : '今日还没有记录' }}</text>
<view v-if="!todayChecked" class="heatmap-foot-cta" @click="goGlucosePage()">
<text>去记血糖</text>
<view class="heatmap-foot-cta" @click="openInputForm()">
<text>{{ todayChecked ? '编辑血糖' : '去记血糖' }}</text>
</view>
</view>
</view>
@@ -173,7 +173,7 @@
<view
class="record-quick-chip"
:class="{ done: todayChecked, pending: !todayChecked }"
@click="goGlucosePage()"
@click="openInputForm()"
>
<view class="record-quick-icon-wrap">
<TongjiIcon name="glucose" size="md" :color="todayChecked ? '#386641' : '#204E2B'" />
@@ -501,6 +501,104 @@
<text>数据每日同步 · 阈值仅作参考请遵医嘱</text>
</view>
<!-- 录入今日血糖 -->
<view v-if="inputForm.visible" class="input-modal-mask" @click="closeInputForm">
<view class="input-modal" @click.stop>
<view class="input-modal-grip" />
<view class="input-modal-head">
<view class="input-modal-title-wrap">
<text class="input-modal-title">{{ inputForm.id ? '修改今日血糖' : '录入今日血糖' }}</text>
<text class="input-modal-sub">{{ todayText }} · {{ patientName || '本人' }}</text>
</view>
<view class="input-modal-close" @click="closeInputForm">
<text class="input-modal-close-icon">×</text>
</view>
</view>
<view v-if="inputForm.loading" class="input-modal-loading">
<text>加载今日数据中</text>
</view>
<scroll-view v-else scroll-y class="input-modal-body input-modal-body-ready">
<view class="input-modal-tip">
<text>请如实填写留空表示该项不上传当天可多次修改</text>
</view>
<view class="input-section-title">血糖mmol/L</view>
<view class="input-field">
<view class="input-field-label">
<view class="input-field-dot input-field-dot-fasting" />
<text>空腹血糖</text>
</view>
<view class="input-field-input">
<input
type="digit"
:value="inputForm.fasting_blood_sugar"
placeholder="如 6.5"
placeholder-class="input-placeholder"
@input="onInputField('fasting_blood_sugar', $event)"
/>
</view>
</view>
<view class="input-field">
<view class="input-field-label">
<view class="input-field-dot input-field-dot-postprandial" />
<text>餐后血糖</text>
</view>
<view class="input-field-input">
<input
type="digit"
:value="inputForm.postprandial_blood_sugar"
placeholder="如 8.0"
placeholder-class="input-placeholder"
@input="onInputField('postprandial_blood_sugar', $event)"
/>
</view>
</view>
<view class="input-field">
<view class="input-field-label">
<view class="input-field-dot input-field-dot-other" />
<text>其他血糖</text>
</view>
<view class="input-field-input">
<input
type="digit"
:value="inputForm.other_blood_sugar"
placeholder="如 7.2"
placeholder-class="input-placeholder"
@input="onInputField('other_blood_sugar', $event)"
/>
</view>
</view>
<view class="input-section-title">备注</view>
<view class="input-field input-field-textarea">
<textarea
:value="inputForm.remark"
placeholder="如:饭前空腹测量、餐后2小时测量等(选填)"
placeholder-class="input-placeholder"
:show-confirm-bar="false"
:auto-height="true"
@input="onInputField('remark', $event)"
/>
</view>
</scroll-view>
<view class="input-modal-actions">
<view
v-if="inputForm.id"
class="input-modal-btn danger"
:class="{ disabled: inputForm.submitting }"
@click="deleteInputRecord"
>
<text>删除</text>
</view>
<view
class="input-modal-btn primary"
:class="{ disabled: inputForm.submitting || inputForm.loading }"
@click="submitInputForm"
>
<text>{{ inputForm.submitting ? '提交中…' : (inputForm.id ? '保存修改' : '提交录入') }}</text>
</view>
</view>
</view>
</view>
<!-- 录入血压弹层 -->
<view v-if="bpForm.visible" class="input-modal-mask" @click="closeBpForm">
<view class="input-modal" @click.stop>
@@ -1576,6 +1674,18 @@ const todayText = computed(() => {
return `${d.getFullYear()}-${m}-${day} ${weekdays[d.getDay()]}`
})
const emptyInputForm = () => ({
visible: false,
loading: false,
submitting: false,
id: 0,
fasting_blood_sugar: '',
postprandial_blood_sugar: '',
other_blood_sugar: '',
remark: ''
})
const inputForm = ref(emptyInputForm())
const emptyBpForm = () => ({
visible: false,
loading: false,
@@ -1593,6 +1703,124 @@ function prepareRecordOverlay() {
ttsStop()
}
function onInputField(field, e) {
inputForm.value[field] = e?.detail?.value ?? ''
}
async function openInputForm() {
if (!diagnosisId.value) {
showUserToast('请先选择就诊卡')
return
}
const token = uni.getStorageSync('token')
if (!token) {
showUserToast('请先登录后再录入')
return
}
prepareRecordOverlay()
inputForm.value = { ...emptyInputForm(), visible: true, loading: true }
try {
const res = await proxy.apiUrl({
url: '/api/tcm/dailyTodayBloodRecord',
method: 'GET',
data: { diagnosis_id: diagnosisId.value }
}, false)
if (res && res.code === 1 && res.data && res.data.record) {
const r = res.data.record
inputForm.value.id = Number(r.id) || 0
inputForm.value.fasting_blood_sugar = numToStr(r.fasting_blood_sugar)
inputForm.value.postprandial_blood_sugar = numToStr(r.postprandial_blood_sugar)
inputForm.value.other_blood_sugar = numToStr(r.other_blood_sugar)
inputForm.value.remark = String(r.remark || '')
}
} catch (e) {
/* 静默:拉取失败时仍允许新增录入 */
} finally {
inputForm.value.loading = false
}
}
function closeInputForm() {
if (inputForm.value.submitting) return
inputForm.value.visible = false
}
async function submitInputForm() {
if (inputForm.value.submitting || inputForm.value.loading) return
const f = inputForm.value
const hasAny = [
f.fasting_blood_sugar, f.postprandial_blood_sugar, f.other_blood_sugar
].some((v) => v !== '' && v !== null && Number(v) > 0) || !!(f.remark && f.remark.trim())
if (!hasAny) {
showUserToast('请至少填写一项数据')
return
}
inputForm.value.submitting = true
try {
const res = await proxy.apiUrl({
url: '/api/tcm/dailySaveBloodRecord',
method: 'POST',
data: {
diagnosis_id: diagnosisId.value,
fasting_blood_sugar: f.fasting_blood_sugar,
postprandial_blood_sugar: f.postprandial_blood_sugar,
other_blood_sugar: f.other_blood_sugar,
remark: f.remark
}
}, false)
if (res && res.code === 1) {
showUserToast(res.msg || '已保存', { icon: 'success' })
inputForm.value.visible = false
await fetchAll()
await fetchGamifyState()
if (claimablePoints.value > 0) {
showUserToast('血糖已记录,点击浇水领取积分', { duration: 2800 })
}
} else {
showUserToast((res && res.msg) || '保存失败')
}
} catch (e) {
showUserToast('网络异常,请稍后再试')
} finally {
inputForm.value.submitting = false
}
}
function deleteInputRecord() {
if (!inputForm.value.id || inputForm.value.submitting) return
uni.showModal({
title: '删除确认',
content: '确定删除今天已录入的血糖数据吗?删除后无法恢复。',
confirmColor: '#dc2626',
success: async (m) => {
if (!m.confirm) return
inputForm.value.submitting = true
try {
const res = await proxy.apiUrl({
url: '/api/tcm/dailyDeleteBloodRecord',
method: 'POST',
data: {
diagnosis_id: diagnosisId.value,
id: inputForm.value.id
}
}, false)
if (res && res.code === 1) {
uni.showToast({ title: '已删除', icon: 'success' })
inputForm.value.visible = false
await fetchAll()
await fetchGamifyState()
} else {
showUserToast((res && res.msg) || '删除失败')
}
} catch (e) {
uni.showToast({ title: '网络异常,请稍后再试', icon: 'none' })
} finally {
inputForm.value.submitting = false
}
}
})
}
function onBpField(field, e) {
const val = e?.detail?.value ?? ''
bpForm.value[field] = val
@@ -1760,7 +1988,7 @@ const exerciseForm = ref(emptyExerciseForm())
/** 任一录入弹层打开时需卸载 canvas(小程序原生组件会盖住普通 view)*/
const recordOverlayOpen = computed(() =>
bpForm.value.visible || dietForm.value.visible || exerciseForm.value.visible
inputForm.value.visible || bpForm.value.visible || dietForm.value.visible || exerciseForm.value.visible
)
function onExerciseField(field, e) {
@@ -3082,7 +3310,7 @@ function onGamifyTaskTap(task) {
}
if (!task.completed) {
if (task.id === 'blood') {
goGlucosePage()
openInputForm()
return
}
if (task.id === 'diet') {
@@ -4843,6 +5071,7 @@ function onFamilyLikeStripTap() {
display: flex;
align-items: flex-end;
justify-content: center;
animation: input-modal-mask-in 0.38s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.input-modal {
width: 100%;
@@ -4854,13 +5083,22 @@ function onFamilyLikeStripTap() {
max-height: 88vh;
display: flex;
flex-direction: column;
animation: input-modal-rise 0.28s cubic-bezier(0.16, 1, 0.3, 1);
animation: input-modal-rise 0.46s cubic-bezier(0.22, 1, 0.36, 1) both;
position: relative;
z-index: 1000;
will-change: transform, opacity;
}
@keyframes input-modal-mask-in {
from { opacity: 0; }
to { opacity: 1; }
}
@keyframes input-modal-rise {
from { transform: translateY(60rpx); opacity: 0.5; }
to { transform: translateY(0); opacity: 1; }
from { transform: translate3d(0, 100%, 0); opacity: 0.96; }
to { transform: translate3d(0, 0, 0); opacity: 1; }
}
@keyframes input-modal-content-in {
from { opacity: 0; transform: translate3d(0, 24rpx, 0); }
to { opacity: 1; transform: translate3d(0, 0, 0); }
}
.input-modal-grip {
width: 80rpx;
@@ -4912,19 +5150,27 @@ function onFamilyLikeStripTap() {
font-weight: 700;
}
.input-modal-loading {
padding: 80rpx 0;
min-height: 520rpx;
padding: 120rpx 0;
text-align: center;
color: var(--slate-600);
font-size: 30rpx;
display: flex;
align-items: center;
justify-content: center;
}
.input-modal-body {
flex: 1;
min-height: 520rpx;
padding: 20rpx 32rpx 28rpx;
overflow-y: auto;
overflow-x: hidden;
box-sizing: border-box;
width: 100%;
}
.input-modal-body-ready {
animation: input-modal-content-in 0.34s cubic-bezier(0.22, 1, 0.36, 1) both;
}
.input-modal-tip {
background: var(--primary-light);
border: 1rpx dashed #99f6e4;