更新
This commit is contained in:
@@ -45,20 +45,19 @@
|
||||
</view>
|
||||
</scroll-view>
|
||||
|
||||
|
||||
|
||||
<view class="st-main">
|
||||
<!-- 1. 录入今日血糖 -->
|
||||
|
||||
|
||||
<!-- 2. 血糖趋势 -->
|
||||
<view v-if="diagnosisId" class="st-chart-card">
|
||||
<!-- 2. 血糖趋势(无就诊卡或录入不足 7 天显示示例曲线) -->
|
||||
<view class="st-chart-card">
|
||||
<view class="st-chart-head">
|
||||
<view>
|
||||
<text class="st-chart-title">血糖趋势</text>
|
||||
<text class="st-chart-sub">{{ rangeText }} · mmol/L</text>
|
||||
<view class="st-chart-more" @click="goRecordsPage">
|
||||
<text class="st-chart-more-text">查看更多记录</text>
|
||||
<TongjiIcon name="chevron-right" size="sm" color="#006c49" />
|
||||
</view>
|
||||
|
||||
</view>
|
||||
<view class="st-chart-legend">
|
||||
<view
|
||||
@@ -79,97 +78,39 @@
|
||||
</view>
|
||||
</view>
|
||||
</view>
|
||||
<view v-if="bloodHasData" class="st-chart-summary">
|
||||
<view class="st-chart-summary-item">
|
||||
<text class="st-chart-summary-num is-high">{{ bloodHighDayCount }}</text>
|
||||
<text class="st-chart-summary-label">天偏高</text>
|
||||
</view>
|
||||
<view class="st-chart-summary-item">
|
||||
<text class="st-chart-summary-num is-good">{{ bloodNormalDayCount }}</text>
|
||||
<text class="st-chart-summary-label">天正常</text>
|
||||
</view>
|
||||
<view class="st-chart-summary-item">
|
||||
<text class="st-chart-summary-num">{{ bloodRecordDayCount }}</text>
|
||||
<text class="st-chart-summary-label">天有记录</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="st-chart-area">
|
||||
<view class="st-chart-area" @click="onChartTap">
|
||||
<canvas
|
||||
v-if="!recordOverlayOpen"
|
||||
id="bloodWaveChart"
|
||||
type="2d"
|
||||
class="st-chart-canvas"
|
||||
@touchstart="onChartTouch"
|
||||
@touchmove="onChartTouch"
|
||||
@click.stop="onChartTap"
|
||||
/>
|
||||
<view v-if="recordOverlayOpen" class="st-chart-placeholder">
|
||||
<text>图表已暂停显示</text>
|
||||
</view>
|
||||
<view v-if="!bloodHasData && !loading && !recordOverlayOpen" class="st-chart-empty">
|
||||
<view v-if="!chartHasDisplayData && !loading && !recordOverlayOpen" class="st-chart-empty">
|
||||
<text>当前时间范围暂无血糖数据</text>
|
||||
</view>
|
||||
<cover-view v-if="hoverInfo && !recordOverlayOpen" class="chart-tip" :style="{ left: hoverInfo.left + 'px', top: hoverInfo.top + 'px' }">
|
||||
<cover-view class="chart-tip-date">{{ hoverInfo.date }}</cover-view>
|
||||
<cover-view v-if="hoverInfo.fasting != null" class="chart-tip-row">
|
||||
<cover-view class="st-dot st-dot-fasting" />
|
||||
<cover-view>空腹 {{ hoverInfo.fasting }}</cover-view>
|
||||
</cover-view>
|
||||
<cover-view v-if="hoverInfo.postprandial != null" class="chart-tip-row">
|
||||
<cover-view class="st-dot st-dot-post" />
|
||||
<cover-view>餐后 {{ hoverInfo.postprandial }}</cover-view>
|
||||
</cover-view>
|
||||
</cover-view>
|
||||
</view>
|
||||
<view class="st-cta" @click="openInputForm()">
|
||||
<TongjiIcon name="plus" size="md" color="#FFFFFF" />
|
||||
<text>录入今日血糖</text>
|
||||
</view>
|
||||
|
||||
<view class="st-game-entry" @click="goGamePage">
|
||||
<view class="st-game-entry-icon">
|
||||
<TongjiIcon name="activity" size="md" color="#006c49" />
|
||||
<view v-if="chartUseMockData" class="st-chart-foot">
|
||||
<text class="st-chart-mock-hint">示例数据,连续记录满 7 天后显示您的真实趋势</text>
|
||||
</view>
|
||||
<view class="st-game-entry-body">
|
||||
<text class="st-game-entry-title">糖分突袭</text>
|
||||
<text class="st-game-entry-sub">消除棋盘食物,边玩边学控糖</text>
|
||||
</view>
|
||||
<TongjiIcon name="chevron-right" size="sm" color="#64748b" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 3. 最新测量 & 概览 -->
|
||||
<view class="st-latest-section">
|
||||
<view
|
||||
v-if="latestMeasurement.hasValue"
|
||||
class="st-latest-hero"
|
||||
:class="{ 'is-high': latestMeasurement.isHigh }"
|
||||
>
|
||||
<view class="st-latest-hero-pattern" aria-hidden="true" />
|
||||
<text class="st-latest-label">最新测量 · {{ latestMeasurement.typeLabel }}</text>
|
||||
<view class="st-latest-value-row">
|
||||
<text class="st-latest-value">{{ latestMeasurement.value }}</text>
|
||||
<text class="st-latest-unit">mmol/L</text>
|
||||
<view class="st-game-entry st-game-entry--in-card" @click="goGamePage">
|
||||
<view class="st-game-entry-icon">
|
||||
<TongjiIcon name="activity" size="sm" color="#006c49" />
|
||||
</view>
|
||||
<view v-if="latestMeasurement.isHigh" class="st-latest-flag">
|
||||
<TongjiIcon name="alert-triangle" size="sm" color="#93000a" />
|
||||
<text>偏高,请遵医嘱</text>
|
||||
</view>
|
||||
</view>
|
||||
<view class="st-summary-grid">
|
||||
<view class="st-summary-card">
|
||||
<text class="st-summary-label">空腹</text>
|
||||
<text class="st-summary-value" :class="{ 'is-high': elderFasting.isHigh }">{{ elderFasting.value }}</text>
|
||||
<text v-if="elderFasting.isHigh" class="st-summary-tag">偏高</text>
|
||||
</view>
|
||||
<view class="st-summary-card">
|
||||
<text class="st-summary-label">餐后</text>
|
||||
<text class="st-summary-value" :class="{ 'is-high': elderPostprandial.isHigh }">{{ elderPostprandial.value }}</text>
|
||||
<text v-if="elderPostprandial.isHigh" class="st-summary-tag">偏高</text>
|
||||
<view class="st-game-entry-body">
|
||||
<text class="st-game-entry-title">识糖小课堂</text>
|
||||
<text class="st-game-entry-sub">认一认:哪些食物含糖高</text>
|
||||
</view>
|
||||
<TongjiIcon name="chevron-right" size="sm" color="#64748b" />
|
||||
</view>
|
||||
</view>
|
||||
|
||||
<!-- 4. AI 饮食建议 -->
|
||||
<view class="st-training-module">
|
||||
<dev-training-entry inline />
|
||||
</view>
|
||||
<!-- 3. AI 饮食建议 -->
|
||||
<view v-if="diagnosisId" class="st-ai-section">
|
||||
<view class="st-ai-deco" aria-hidden="true" />
|
||||
<view class="st-ai-head">
|
||||
@@ -227,7 +168,7 @@
|
||||
|
||||
<view class="st-main-spacer" />
|
||||
</view>
|
||||
|
||||
|
||||
<!-- 收起后的重新唤起按钮 -->
|
||||
<view v-if="diagnosisId && floatAskClosed" class="st-float-reopen" @click="floatAskClosed = false">
|
||||
<TongjiIcon name="sparkles" size="sm" color="#ffffff" />
|
||||
@@ -458,6 +399,7 @@ import TongjiIcon from '../components/TongjiIcon.vue'
|
||||
import { useDietAi } from '../composables/useDietAi.js'
|
||||
import { useSpeechToText } from '../composables/useSpeechToText.js'
|
||||
import TabBarDock from '@/components/app-tab-bar/tab-bar-dock.vue'
|
||||
import DevTrainingEntry from '@/components/dev-training-entry/index.vue'
|
||||
|
||||
const { proxy } = getCurrentInstance()
|
||||
|
||||
@@ -859,6 +801,7 @@ const bloodHasData = computed(() => {
|
||||
})
|
||||
})
|
||||
|
||||
/** 当前时间范围内有真实录入的天数 */
|
||||
const bloodRecordDayCount = computed(() => {
|
||||
return chartDates.value.filter((date) => {
|
||||
const b = bloodByDate.value[date]
|
||||
@@ -866,6 +809,50 @@ const bloodRecordDayCount = computed(() => {
|
||||
}).length
|
||||
})
|
||||
|
||||
const REAL_BLOOD_DAYS_MIN = 7
|
||||
|
||||
/** 无就诊卡或真实录入不足 7 天时,图表用示例数据 */
|
||||
const chartUseMockData = computed(() => {
|
||||
if (!diagnosisId.value) return true
|
||||
return bloodRecordDayCount.value < REAL_BLOOD_DAYS_MIN
|
||||
})
|
||||
|
||||
/** 示例曲线:7 天内有波动的空腹/餐后,便于老人理解趋势图形态 */
|
||||
const mockBloodByDate = computed(() => {
|
||||
const map = {}
|
||||
const dates = chartDates.value
|
||||
dates.forEach((date, i) => {
|
||||
const t = dates.length > 1 ? i / (dates.length - 1) : 0
|
||||
const fasting = Number((5.2 + Math.sin(t * Math.PI) * 0.5 + (i % 2) * 0.15).toFixed(1))
|
||||
const post = Number((7.6 + Math.cos(t * Math.PI) * 1.1 + (i % 3) * 0.25).toFixed(1))
|
||||
const row = {
|
||||
record_date: date,
|
||||
fasting_blood_sugar: fasting,
|
||||
postprandial_blood_sugar: post,
|
||||
other_blood_sugar: '',
|
||||
systolic_pressure: '',
|
||||
diastolic_pressure: ''
|
||||
}
|
||||
row.isHighFasting = isHighFasting(row.fasting_blood_sugar)
|
||||
row.isHighPostprandial = isHighPostprandial(row.postprandial_blood_sugar)
|
||||
row.isHighOther = false
|
||||
row.isHighBp = false
|
||||
row.bpText = '—'
|
||||
map[date] = row
|
||||
})
|
||||
return map
|
||||
})
|
||||
|
||||
/** 图表绘制用数据源:示例或真实 */
|
||||
const chartBloodByDate = computed(() => (
|
||||
chartUseMockData.value ? mockBloodByDate.value : bloodByDate.value
|
||||
))
|
||||
|
||||
const chartHasDisplayData = computed(() => {
|
||||
if (chartUseMockData.value) return chartDates.value.length > 0
|
||||
return bloodHasData.value
|
||||
})
|
||||
|
||||
const bloodHighDayCount = computed(() => {
|
||||
return chartDates.value.filter((date) => {
|
||||
const b = bloodByDate.value[date]
|
||||
@@ -1377,16 +1364,26 @@ function onRefresh() {
|
||||
fetchAll()
|
||||
}
|
||||
|
||||
async function goRecordsPage() {
|
||||
if (!diagnosisId.value) {
|
||||
const ok = await ensureCanRecordGlucose()
|
||||
if (!ok || !diagnosisId.value) return
|
||||
}
|
||||
function buildMorePageUrl() {
|
||||
if (!diagnosisId.value) return '/tongji/pages/more'
|
||||
const q = [
|
||||
`diagnosis_id=${diagnosisId.value}`,
|
||||
patientId.value ? `patient_id=${patientId.value}` : ''
|
||||
].filter(Boolean).join('&')
|
||||
uni.navigateTo({ url: `/tongji/pages/more?${q}` })
|
||||
return `/tongji/pages/more?${q}`
|
||||
}
|
||||
|
||||
/** 点击血糖趋势图:进入记录页 more(无就诊卡时先手机号快捷建档) */
|
||||
async function onChartTap() {
|
||||
if (!diagnosisId.value) {
|
||||
const ok = await ensureCanRecordGlucose()
|
||||
if (!ok || !diagnosisId.value) return
|
||||
}
|
||||
uni.navigateTo({ url: buildMorePageUrl() })
|
||||
}
|
||||
|
||||
async function goRecordsPage() {
|
||||
onChartTap()
|
||||
}
|
||||
|
||||
async function goMorePage() {
|
||||
@@ -1394,7 +1391,7 @@ async function goMorePage() {
|
||||
const ok = await ensureCanRecordGlucose()
|
||||
if (!ok || !diagnosisId.value) return
|
||||
}
|
||||
uni.navigateTo({ url: '/tongji/pages/more' })
|
||||
uni.navigateTo({ url: buildMorePageUrl() })
|
||||
}
|
||||
|
||||
function goGamePage() {
|
||||
@@ -2102,7 +2099,7 @@ function scheduleChartRedraw() {
|
||||
|
||||
function buildSeries(field) {
|
||||
return chartDates.value.map((date) => {
|
||||
const b = bloodByDate.value[date]
|
||||
const b = chartBloodByDate.value[date]
|
||||
return b ? toNumber(b[field]) : null
|
||||
})
|
||||
}
|
||||
@@ -2136,7 +2133,7 @@ function drawChart() {
|
||||
|
||||
ctx.clearRect(0, 0, w, h)
|
||||
|
||||
const padding = { left: 40, right: 18, top: 26, bottom: 36 }
|
||||
const padding = { left: 40, right: 18, top: 12, bottom: 28 }
|
||||
const cw = w - padding.left - padding.right
|
||||
const ch = h - padding.top - padding.bottom
|
||||
|
||||
@@ -2392,59 +2389,25 @@ function toggleChartSeries(key) {
|
||||
scheduleChartRedraw()
|
||||
}
|
||||
|
||||
function onChartTouch(e) {
|
||||
if (!chartCoords || !canvasWidth) return
|
||||
if (!chartDates.value.length) return
|
||||
let touchX = 0
|
||||
let touchY = 0
|
||||
if (e && e.touches && e.touches[0]) {
|
||||
touchX = e.touches[0].x ?? e.touches[0].clientX ?? 0
|
||||
touchY = e.touches[0].y ?? e.touches[0].clientY ?? 0
|
||||
}
|
||||
const { padding, cw, n } = chartCoords
|
||||
if (touchX < padding.left || touchX > padding.left + cw) {
|
||||
return
|
||||
}
|
||||
const ratio = n <= 1 ? 0 : (touchX - padding.left) / cw
|
||||
let idx = Math.round(ratio * (n - 1))
|
||||
if (idx < 0) idx = 0
|
||||
if (idx > n - 1) idx = n - 1
|
||||
const date = chartDates.value[idx]
|
||||
const fp = chartSeriesVisible.value.fasting ? chartCoords.fasting[idx] : null
|
||||
const pp = chartSeriesVisible.value.postprandial ? chartCoords.postprandial[idx] : null
|
||||
const anchorX = fp?.x ?? pp?.x ?? padding.left + cw * (idx / Math.max(1, n - 1))
|
||||
|
||||
const left = Math.max(8, Math.min(canvasWidth - 130, anchorX - 60))
|
||||
const top = Math.max(4, touchY - 70)
|
||||
|
||||
hoverInfo.value = {
|
||||
date: date.slice(5),
|
||||
fasting: fp?.value ?? null,
|
||||
postprandial: pp?.value ?? null,
|
||||
left,
|
||||
top
|
||||
}
|
||||
if (hoverTimer) clearTimeout(hoverTimer)
|
||||
hoverTimer = setTimeout(() => {
|
||||
hoverInfo.value = null
|
||||
}, 2500)
|
||||
}
|
||||
|
||||
watch(rangeMode, () => {
|
||||
scheduleChartRedraw()
|
||||
})
|
||||
|
||||
watch([chartUseMockData, bloodRecords], () => {
|
||||
scheduleChartRedraw()
|
||||
})
|
||||
|
||||
// ============ 生命周期 ============
|
||||
function readUserContext() {
|
||||
try {
|
||||
const userData = uni.getStorageSync('userData')
|
||||
if (userData && userData.diagnosis) {
|
||||
if (!patientId.value) patientId.value = Number(userData.diagnosis.patient_id) || 0
|
||||
if (!diagnosisId.value) diagnosisId.value = Number(userData.diagnosis.id) || 0
|
||||
if (!patientName.value) patientName.value = userData.diagnosis.patient_name || ''
|
||||
if (!age.value) age.value = Number(userData.diagnosis.age) || 0
|
||||
if (!gender.value) gender.value = Number(userData.diagnosis.gender) || 0
|
||||
}
|
||||
if (!userData) return
|
||||
// 勿用问诊会话里的 diagnosis 作为日常血糖诊单(无 view_records 会报无权)
|
||||
const name = String(userData.real_name || userData.nickname || '').trim()
|
||||
if (!patientName.value && name) patientName.value = name
|
||||
const sex = Number(userData.sex)
|
||||
if (!gender.value && (sex === 1 || sex === 2)) gender.value = sex === 1 ? 1 : 0
|
||||
if (!age.value && userData.age) age.value = Number(userData.age) || 0
|
||||
} catch (e) {}
|
||||
}
|
||||
|
||||
@@ -2527,14 +2490,12 @@ async function bootstrap(options) {
|
||||
if (!target) target = list[0]
|
||||
await applyCard(target)
|
||||
} else {
|
||||
if (passedDiagnosisId) diagnosisId.value = passedDiagnosisId
|
||||
if (passedPatientId) patientId.value = passedPatientId
|
||||
diagnosisId.value = passedDiagnosisId || 0
|
||||
patientId.value = passedPatientId || 0
|
||||
if (passedPatientName) patientName.value = passedPatientName
|
||||
if (passedAge) age.value = passedAge
|
||||
if (passedGender) gender.value = passedGender
|
||||
if (!diagnosisId.value || !patientId.value) {
|
||||
readUserContext()
|
||||
}
|
||||
if (!patientName.value) readUserContext()
|
||||
readUserMobile()
|
||||
if (!diagnosisId.value && userMobile.value) {
|
||||
await ensureDailyContextByPhone()
|
||||
@@ -2544,6 +2505,10 @@ async function bootstrap(options) {
|
||||
authChecking.value = false
|
||||
if (diagnosisId.value) {
|
||||
await fetchAll()
|
||||
} else {
|
||||
bloodRecords.value = []
|
||||
await nextTick()
|
||||
scheduleChartRedraw()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user