From aecfd2a254e54966f08f62b2adf643358875570f Mon Sep 17 00:00:00 2001 From: fuenmao <980740792@qq.com> Date: Wed, 23 Sep 2026 18:19:47 +0800 Subject: [PATCH] 111 --- src/components/AppTabBar.vue | 19 +++ src/router.ts | 2 + src/services/health.ts | 42 ++++- src/views/AssessmentView.vue | 270 +++----------------------------- src/views/DashboardView.vue | 33 ++-- src/views/DeviceControlView.vue | 160 +++++++++++++++++-- src/views/ProfileView.vue | 38 +++-- src/views/TcmAssessmentView.vue | 209 ++++++++++++++++++++++++ 8 files changed, 491 insertions(+), 282 deletions(-) create mode 100644 src/views/TcmAssessmentView.vue diff --git a/src/components/AppTabBar.vue b/src/components/AppTabBar.vue index 24ba2c9..1b2c957 100644 --- a/src/components/AppTabBar.vue +++ b/src/components/AppTabBar.vue @@ -8,6 +8,7 @@ const router = useRouter() const currentTab = computed(() => { const path = route.path if (path.startsWith('/assessment')) return 'assessment' + if (path.startsWith('/tcm-assessment')) return 'tcm' if (path.startsWith('/profile')) return 'profile' return 'home' }) @@ -17,6 +18,8 @@ function navigate(target: string) { router.replace('/dashboard') } else if (target === 'assessment' && route.path !== '/assessment') { router.replace('/assessment') + } else if (target === 'tcm' && route.path !== '/tcm-assessment') { + router.replace('/tcm-assessment') } else if (target === 'profile' && route.path !== '/profile') { router.replace('/profile') } @@ -39,6 +42,22 @@ function navigate(target: string) { 首页 +
+
+ + + + + + +
+ 中医四诊 +
+
+ notes: Record +} + +export type TcmAssessmentResult = { + hasCompleted: boolean + constitutionCode: string + constitutionName: string + secondaryName?: string | null + summary: string + aromaRecommendation: string + musicRecommendation: string + roomTemperatureRecommendation: string + scores: Record + answers: Record + notes: Record + sceneConfig: PersonalizedSceneConfig + assessedAt?: string | null } export type HealthDeviceCategory = @@ -251,6 +286,9 @@ export const healthApi = { getAssessment: () => http.get('/api/health/assessment'), saveAssessment: (payload: SaveHealthAssessmentInput) => http.post('/api/health/assessment', payload), + getTcmAssessment: () => http.get('/api/health/tcm-assessment'), + saveTcmAssessment: (payload: TcmAssessmentInput) => + http.post('/api/health/tcm-assessment', payload), homeAssistantStatus: () => http.get('/api/health/home-assistant/status'), lightTransitionStatus: () => @@ -266,7 +304,7 @@ export const healthApi = { playSpeakerDemo: () => http.post('/api/health/home-assistant/speaker/play-demo'), executeSleepScene: () => - http.post('/api/health/home-assistant/scenes/sleep'), + http.post('/api/health/home-assistant/scenes/sleep', undefined, { timeout: 90000 }), executeAllOffScene: () => http.post('/api/health/home-assistant/scenes/all-off'), switchDevice: (payload: { deviceId: string; turnOn: boolean; entityId?: string }) => diff --git a/src/views/AssessmentView.vue b/src/views/AssessmentView.vue index a221378..556e344 100644 --- a/src/views/AssessmentView.vue +++ b/src/views/AssessmentView.vue @@ -6,8 +6,7 @@ import AppTabBar from '../components/AppTabBar.vue' import { healthApi, type HealthAssessmentAnswers, - type HealthProfile, - type PersonalizedSceneConfig + type HealthProfile } from '../services/health' const router = useRouter() @@ -55,129 +54,6 @@ const bmiStatus = computed(() => { return { label: '肥胖预警', color: '#f56c6c' } }) -// 实时前端预计算家具参数 -const liveSceneConfig = computed(() => { - let humidity = 52 - let fan = '1挡' - let mattressTemp = 35.5 - let mattressHeating = 2 - if (answers.temperaturePreference === 'heat_fear') { - humidity = 48 - fan = '2挡' - mattressTemp = 32.0 - mattressHeating = 1 - } else if (answers.temperaturePreference === 'cold_fear') { - humidity = 58 - fan = '1挡' - mattressTemp = 37.5 - mattressHeating = 3 - } - - let lightBrightness = 18 - let lightColorTemp = 2700 - let fragranceLevel = 1 - let speakerSleep = true - let speakerMode = '舒缓轻音乐' - let speakerVol = 15 - - if (answers.fallingAsleepSpeed === 'hard') { - lightBrightness = 8 - lightColorTemp = 2200 - fragranceLevel = 2 - speakerSleep = true - speakerMode = '助眠白噪音' - speakerVol = 12 - } else if (answers.fallingAsleepSpeed === 'fast') { - lightBrightness = 25 - lightColorTemp = 3000 - fragranceLevel = 1 - speakerSleep = false - speakerMode = '关闭助眠音频' - speakerVol = 10 - } - - if (answers.sleepDuration === '<6') { - lightBrightness -= 3 - speakerVol += 3 - fragranceLevel += 1 - } else if (answers.sleepDuration === '6-7') { - lightBrightness -= 1 - speakerVol += 1 - } else if (answers.sleepDuration === '>8') { - lightBrightness += 2 - speakerVol -= 2 - } - - let leftFirm = 5 - let rightFirm = 5 - let mattressMode = '智能适配' - if (answers.muscleFatigueArea === 'waist_back' || answers.muscleFatigueArea === 'back_neck' || answers.mattressPreference === 'firm') { - leftFirm = 7 - rightFirm = 7 - mattressMode = '智能腰托' - } else if (answers.mattressPreference === 'soft') { - leftFirm = 4 - rightFirm = 4 - mattressMode = '瑜伽助眠' - } else if (answers.muscleFatigueArea === 'legs') { - leftFirm = 6 - rightFirm = 6 - } - - if (answers.nightAwakening === 'frequent') { - fan = '1挡' - lightBrightness -= 2 - } - if (answers.exerciseFrequency === 'none') mattressHeating += 1 - if (answers.exerciseFrequency === 'daily') mattressHeating -= 1 - if (answers.activeTimeOfDay === 'morning') lightColorTemp += 150 - if (answers.activeTimeOfDay === 'night') { - lightColorTemp -= 250 - lightBrightness -= 2 - } - if (answers.activeTimeOfDay === 'irregular') { - speakerSleep = true - speakerVol += 2 - } - - let uv = answers.lateNightEating === 'often' || answers.lateNightEating === 'sometimes' || answers.flavorPreference === 'heavy' - if (answers.mealRegularity === 'irregular') { - uv = true - fragranceLevel += 1 - } else if (answers.mealRegularity === 'skip_breakfast') { - fragranceLevel += 1 - } - if (answers.dailyWaterIntake === 'scarce') humidity += 3 - if (answers.dailyWaterIntake === 'abundant') humidity -= 2 - - const bmi = computedBmi.value - if (bmi && bmi < 18.5) mattressHeating += 1 - if (bmi && bmi >= 28) humidity -= 2 - if (age.value && age.value >= 60) { - lightBrightness += 3 - speakerVol = Math.max(8, speakerVol - 2) - } - - return { - targetHumidity: Math.min(70, Math.max(40, humidity)), - dehumidifierMode: answers.nightAwakening === 'none' ? '智能' : '睡眠', - fanLevel: fan, - enableUv: uv, - lightBrightnessPercent: Math.min(100, Math.max(1, lightBrightness)), - lightColorTemperatureKelvin: Math.min(5000, Math.max(2200, lightColorTemp)), - mattressTargetTemp: mattressTemp, - mattressLeftFirmness: leftFirm, - mattressRightFirmness: rightFirm, - mattressHeatingLevel: Math.min(4, Math.max(0, mattressHeating)), - mattressMode, - enableFragrance: true, - fragranceLevel: Math.min(3, Math.max(1, fragranceLevel)), - enableSpeakerSleepMode: speakerSleep, - speakerAudioMode: speakerMode, - speakerVolumePercent: Math.min(50, Math.max(1, speakerVol)) - } -}) - async function loadExisting() { loading.value = true try { @@ -244,7 +120,7 @@ async function handleSave() { basicInfo: { ...basicInfo }, answers: { ...answers } }) - showToast({ type: 'success', message: '评估已完成,已更新智能设备方案' }) + showToast({ type: 'success', message: '健康评估已保存' }) router.replace('/dashboard') } catch (err) { showToast(err instanceof Error ? err.message : '保存失败,请重试') @@ -265,7 +141,7 @@ onMounted(() => {
常青树 · 全面身体健康评估

个性化健康评估

- 三大板块均为必选。您的不同身体状态将直接决定卧室除湿机、色温灯、香氛机、助眠音箱及智能床垫的专属启动参数。 + 三大板块均为必选。这里记录您的身体基本情况和生活习惯,用于生成日常健康建议;智能家居启动参数由中医四诊单独生成。

@@ -276,7 +152,7 @@ onMounted(() => { 00

基础体征指标

-

用于计算 BMI 身体质量指数及睡眠承托基准

+

用于记录身体基线并计算 BMI 身体质量指数

@@ -361,7 +237,7 @@ onMounted(() => { 01

睡眠质量板块 (必选)

-

深度关联:香氛开关、灯光色温、音箱睡眠模式、床垫加热与承托模式

+

了解作息、入睡习惯和睡眠感受

@@ -415,7 +291,7 @@ onMounted(() => { >
入睡困难 / 易失眠 (耗时>30分钟) - 定制超暖低亮+音箱睡眠模式 + 入睡较慢

睡前思绪繁杂、对光线声音敏感,常在床上翻来覆去无法入眠

@@ -450,7 +326,7 @@ onMounted(() => {
Q3 - 睡眠体温耐受与体质倾向 + 睡眠时的冷热感受
{ >
燥热偏热 (夜间易出汗、盗汗、怕闷热) - 恒湿48% · 清凉32°C + 偏热

被窝温度稍高就烦躁踢被,需要更干爽清凉的卧房环境

@@ -472,7 +348,7 @@ onMounted(() => { >
畏寒怕冷 (手脚冰凉、极怕冷风) - 恒湿58% · 床垫预热37.5°C + 偏冷

入睡半天手脚捂不热,稍有微风吹拂即觉寒凉

@@ -484,7 +360,7 @@ onMounted(() => { >
冷热适中 (体温感应平衡) - 恒湿52% · 舒适35.5°C + 冷热适中

体感舒适度正常,对温湿度耐受度均衡

@@ -560,7 +436,7 @@ onMounted(() => { 02

运动情况板块 (必选)

-

深度关联:肌肉放松硬度分区、夜间床垫自适应调校

+

了解日常活动频率和身体疲劳感受

@@ -683,7 +559,7 @@ onMounted(() => { 03

饮食情况板块 (必选)

-

深度关联:除湿机紫外线 UV 杀菌深度净化、晚间空气流通

+

了解三餐、夜宵和饮水习惯

@@ -761,9 +637,9 @@ onMounted(() => { >
经常吃夜宵 (睡前2小时内进食) - 开启 UV 紫外深度消杀 + 经常晚间进食
-

胃肠负担大、卧房空气异味残留,需强化除湿机空气净化循环

+

睡前进食可能影响休息,可逐步调整进食时间

{ >
偶尔吃夜宵 (每周 1–2 次加班聚餐) - 按需 UV 杀菌 + 偶尔晚间进食
-

偶尔晚间进食,需要适度空气清新与恒湿防护

+

偶尔因加班或聚餐改变晚餐时间

{ >
坚决不吃夜宵 (睡前3–4小时禁食) - 常规静音 + 较少夜宵
-

良好消化作息,卧房保持超静音柔风环境

+

通常能给晚餐和入睡留出间隔

@@ -824,44 +700,6 @@ onMounted(() => { - -
-
-
算法推演
-

专属全套智能家具启动参数预览

-
-

根据上方填写的体征与三个板块选项,智能中枢已为您定制如下启动预设:

- -
-
- 除湿机恒湿 - {{ liveSceneConfig.targetHumidity }}% 湿度 · {{ liveSceneConfig.fanLevel }} -
-
- 除湿机UV消杀 - - {{ liveSceneConfig.enableUv ? '深度紫外线开启' : '关闭保静音' }} - -
-
- 氛围助眠灯光 - {{ liveSceneConfig.lightBrightnessPercent }}% 亮度 · {{ liveSceneConfig.lightColorTemperatureKelvin }}K 暖光 -
-
- Xiaomi Sound Pro - {{ liveSceneConfig.enableSpeakerSleepMode ? '开启睡眠模式' : '关闭睡眠模式' }} · {{ liveSceneConfig.speakerVolumePercent }}% 音量 -
-
- 8H 智能睡眠床垫 - 左右加热 {{ liveSceneConfig.mattressHeatingLevel }} 档 · {{ liveSceneConfig.mattressMode }} -
-
- 智能香氛机 - 开启香氛与柔光 · 浓度由设备端预设 -
-
-
-
{ :loading="submitting" @click="handleSave" > - 保存评估并应用智能家具参数 + 保存健康评估
@@ -1197,11 +1035,6 @@ onMounted(() => { background: rgba(192, 105, 39, 0.1); } -.tag-uv { - color: #7b42b5; - background: rgba(123, 66, 181, 0.1); -} - .opt-desc { margin: 0; font-size: 11px; @@ -1209,71 +1042,6 @@ onMounted(() => { line-height: 1.5; } -/* 算法推演预览卡片 */ -.result-preview-card { - background: linear-gradient(145deg, #123e37, #09332c); - color: #ffffff; - border: 1px solid rgba(214, 166, 87, 0.3); -} - -.preview-header { - display: flex; - align-items: center; - gap: 8px; - margin-bottom: 6px; -} - -.preview-badge { - background: #d6a657; - color: #123e37; - font-size: 10px; - font-weight: 800; - padding: 2px 6px; - border-radius: 4px; -} - -.preview-header h4 { - margin: 0; - font-size: 15px; - color: #f7ede1; -} - -.preview-intro { - margin: 0 0 14px; - font-size: 12px; - color: rgba(255, 255, 255, 0.72); - line-height: 1.5; -} - -.preset-items-grid { - display: grid; - grid-template-columns: 1fr; - gap: 8px; -} - -.preset-pill { - display: flex; - justify-content: space-between; - align-items: center; - background: rgba(255, 255, 255, 0.08); - border: 1px solid rgba(255, 255, 255, 0.1); - border-radius: 10px; - padding: 8px 12px; - font-size: 12px; -} - -.preset-pill .label { - color: rgba(255, 255, 255, 0.7); -} - -.preset-pill strong { - color: #ffefcf; -} - -.highlight-uv { - color: #dfb2ff !important; -} - .submit-action-bar { margin-top: 20px; margin-bottom: 24px; diff --git a/src/views/DashboardView.vue b/src/views/DashboardView.vue index 92ddb1d..dba8252 100644 --- a/src/views/DashboardView.vue +++ b/src/views/DashboardView.vue @@ -41,6 +41,11 @@ async function load() { } async function executeScene(scene: 'sleep' | 'all-off') { + if (scene === 'sleep' && !dashboard.value?.tcmResult?.hasCompleted) { + showToast('请先完成中医四诊,生成体质倾向') + router.push('/tcm-assessment') + return + } sceneRunning.value = scene try { const result = scene === 'sleep' @@ -82,7 +87,7 @@ onMounted(load)

今日健康建议

{{ dashboard?.currentPlan?.title || '尚未完成健康评估' }}

-

{{ dashboard?.currentPlan?.summary || '完成健康评估后,系统会自动生成个人健康方案与家居启动参数。' }}

+

{{ dashboard?.currentPlan?.summary || '完成健康评估后,系统会整理您的基本情况并生成日常健康建议。' }}

重要提醒
-

先填写个人基本信息与身体评估

-

尚未进行身体及睡眠质量、运动情况、饮食情况三大维度评估。完成评估后将为您专属定制全套智能家居启动参数!

+

完善个人基本信息与健康评估

+

尚未记录身体及睡眠质量、运动情况、饮食情况。健康评估仅用于了解基本情况;设备启动参数由中医四诊体质结果生成。

- -
+ +
- 专属舒睡启动参数 + 体质专属舒睡启动参数
-

依据您的体征评估与冷热、承托偏好,智能中枢已生成专属预设:

+

+ {{ dashboard?.tcmResult?.hasCompleted + ? `依据${dashboard.tcmResult.constitutionName}倾向生成基础预设;床垫加热看重庆天气,软硬度看 BMI。` + : '先完成中医四诊并生成体质倾向,才可一键开启专属场景。' }} + 去完成四诊 › +

-
+
恒湿目标 {{ dashboard?.sceneConfig?.targetHumidity || 55 }}% · {{ dashboard?.sceneConfig?.fanLevel || '1挡' }} @@ -128,19 +138,22 @@ onMounted(load)
8H 床垫 - 左右加热 {{ dashboard?.sceneConfig?.mattressHeatingLevel ?? 1 }} 档 · {{ dashboard?.sceneConfig?.mattressMode || '智能适配' }} + 左右加热 {{ dashboard?.mattressComfort?.heatingLevel ?? 0 }} 档 · 软硬度{{ dashboard?.mattressComfort?.firmness || '待填写 BMI' }}
除湿消杀 UV 深度消杀已开启
+

+ {{ dashboard?.mattressComfort?.weatherReason }};{{ dashboard?.mattressComfort?.firmnessReason }} +

@@ -498,6 +557,9 @@ onUnmounted(() => {
+
{ { />
2200K 超暖橙光 (利于褪黑素) - 6500K 清爽冷白光 + 5000K 清爽白光
@@ -903,6 +965,15 @@ onUnmounted(() => {
+
+ 重庆天气与 BMI 床垫建议 +

{{ mattressComfort?.weatherReason || '天气建议读取中' }}

+

{{ mattressComfort?.firmnessReason || 'BMI 建议读取中' }}

+ + 重庆室外 {{ mattressComfort.outdoorTemperatureCelsius }}℃ · 体感 {{ mattressComfort.feelsLikeCelsius }}℃ + + 自动场景使用上述建议;下面可手动调整,加热滑块属于手动覆盖。 +
@@ -978,6 +1049,46 @@ onUnmounted(() => { >{{ mode }}
+ +
+ 分区软硬度与按摩调节 +

软硬度按左、右各 6 个部位独立设置;BMI 只给出演示起点,并非医学建议。选择按摩部位可能让床垫立即动作。

+
+ + +
+
+ + +
+
+ + +
+
+ + +
+
@@ -1367,6 +1478,25 @@ onUnmounted(() => { line-height: 1.55; } +.mattress-recommendation { + padding: 12px; + border: 1px solid #cde3dc; + border-radius: 12px; + background: #f3faf7; + color: #174d40; + font-size: 12px; + line-height: 1.55; +} +.mattress-recommendation strong { font-size: 13px; } +.mattress-recommendation p { margin: 7px 0 0; } +.mattress-recommendation small { display: block; margin-top: 6px; color: #6a8479; } +.mattress-advanced { border-top: 1px solid #e8efea; padding-top: 12px; } +.mattress-advanced summary { cursor: pointer; color: #0c5045; font-weight: 700; font-size: 14px; } +.mattress-advanced .mattress-mode-note { margin: 12px 0; } +.mattress-select-row { display: flex; align-items: center; justify-content: space-between; gap: 12px; padding: 9px 0; border-bottom: 1px solid #edf1ee; } +.mattress-select-row label { color: #556c64; font-size: 12px; } +.mattress-select-row select { width: 48%; min-height: 36px; border: 1px solid #d9e6de; border-radius: 9px; background: #fff; padding: 5px 8px; color: #174d40; font-size: 12px; } + .light-mode-panel { padding: 12px; border-radius: 12px; @@ -1557,4 +1687,16 @@ onUnmounted(() => { color: #72847d; line-height: 1.6; } + +.tcm-guide-button { + width: 100%; + margin-top: 10px; + border: 1px solid rgba(255, 255, 255, .35); + border-radius: 12px; + background: rgba(255, 255, 255, .12); + color: #fff; + min-height: 40px; + font-size: 12px; + font-weight: 700; +} diff --git a/src/views/ProfileView.vue b/src/views/ProfileView.vue index 68673a7..0623c0b 100644 --- a/src/views/ProfileView.vue +++ b/src/views/ProfileView.vue @@ -53,7 +53,7 @@ const ageDisplay = computed(() => { return age }) -// 根据评估答案提取体质标签 +// 健康评估只展示生活习惯画像;体质由中医四诊独立判定。 const profileTags = computed(() => { const ans = assessment.value?.answers if (!ans || !assessment.value?.hasCompletedAssessment) return [] @@ -61,25 +61,25 @@ const profileTags = computed(() => { const list: { label: string; desc: string; type: 'gold' | 'blue' | 'green' | 'purple' }[] = [] if (ans.temperaturePreference === 'heat_fear') { - list.push({ label: '体感燥热偏温', desc: '除湿机清爽48% · 床垫低档加热', type: 'blue' }) + list.push({ label: '夜间偏热感', desc: '自述夜间较容易感到闷热', type: 'blue' }) } else if (ans.temperaturePreference === 'cold_fear') { - list.push({ label: '体质畏寒怕冷', desc: '除湿机温润58% · 床垫加强加热', type: 'gold' }) + list.push({ label: '夜间偏冷感', desc: '自述睡眠时较容易感到寒冷', type: 'gold' }) } else { - list.push({ label: '冷热适中平衡', desc: '标准舒适温湿度', type: 'green' }) + list.push({ label: '冷热感受适中', desc: '自述夜间温度感受较平稳', type: 'green' }) } if (ans.fallingAsleepSpeed === 'hard') { - list.push({ label: '睡前敏感易失眠', desc: '2200K超暖暗光 · 音箱睡眠模式', type: 'purple' }) + list.push({ label: '入睡较慢', desc: '可关注睡前作息和放松习惯', type: 'purple' }) } else if (ans.fallingAsleepSpeed === 'fast') { - list.push({ label: '快速深度睡眠', desc: '自然转场柔光', type: 'green' }) + list.push({ label: '入睡较快', desc: '通常能较快进入睡眠', type: 'green' }) } if (ans.muscleFatigueArea === 'waist_back' || ans.muscleFatigueArea === 'back_neck') { - list.push({ label: '脊柱腰背劳损', desc: '床垫启用智能腰托模式', type: 'gold' }) + list.push({ label: '腰背疲劳感', desc: '自述腰背或颈部容易疲劳', type: 'gold' }) } if (ans.lateNightEating === 'often' || ans.lateNightEating === 'sometimes' || ans.flavorPreference === 'heavy') { - list.push({ label: '晚间进食空气净化', desc: '除湿机UV紫外消杀已启用', type: 'purple' }) + list.push({ label: '关注晚间饮食', desc: '可逐步调整夜宵与重口味习惯', type: 'purple' }) } return list @@ -174,7 +174,7 @@ onMounted(() => {
-

您尚未完成身体健康评估,全屋智能家具当前使用出厂默认参数运行。

+

您尚未完成身体健康评估;一键舒睡还需完成中医四诊,生成体质倾向。

{
+
+
+

中医四诊体质倾向

+ + {{ dashboard?.tcmResult?.hasCompleted ? '已生成' : '未评估' }} + +
+ +

完成四诊后,才能一键启动体质相关的设备预设。

+ + {{ dashboard?.tcmResult?.hasCompleted ? '重新评估体质' : '去完成四诊' }} + +
+
@@ -225,7 +243,7 @@ onMounted(() => {
> diff --git a/src/views/TcmAssessmentView.vue b/src/views/TcmAssessmentView.vue new file mode 100644 index 0000000..f152aca --- /dev/null +++ b/src/views/TcmAssessmentView.vue @@ -0,0 +1,209 @@ + + +