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 身体质量指数
- 深度关联:肌肉放松硬度分区、夜间床垫自适应调校
+ 了解日常活动频率和身体疲劳感受
@@ -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。`
+ : '先完成中医四诊并生成体质倾向,才可一键开启专属场景。' }}
+ 去完成四诊 ›
+
-
@@ -903,6 +965,15 @@ onUnmounted(() => {
+
+
重庆天气与 BMI 床垫建议
+
{{ mattressComfort?.weatherReason || '天气建议读取中' }}
+
{{ mattressComfort?.firmnessReason || 'BMI 建议读取中' }}
+
+ 重庆室外 {{ mattressComfort.outdoorTemperatureCelsius }}℃ · 体感 {{ mattressComfort.feelsLikeCelsius }}℃
+
+
自动场景使用上述建议;下面可手动调整,加热滑块属于手动覆盖。
+
+
+
+ 分区软硬度与按摩调节
+ 软硬度按左、右各 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.constitutionName }}{{ dashboard.tcmResult.secondaryName ? ` · 兼${dashboard.tcmResult.secondaryName}` : '' }}
+ {{ dashboard.tcmResult.summary }}
+ 香型建议:{{ dashboard.tcmResult.aromaRecommendation }};音乐建议:{{ dashboard.tcmResult.musicRecommendation }}
+
+ 完成四诊后,才能一键启动体质相关的设备预设。
+
+ {{ 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 @@
+
+
+
+
+
+
+
+ 当前体质倾向演示参考 · 非医学诊断
+ {{ result.constitutionName }}
+ 次要倾向:{{ result.secondaryName }}
+ {{ result.summary }}
+
+
香型建议{{ result.aromaRecommendation }}
+
音乐风格建议{{ result.musicRecommendation }}
+
室温建议{{ result.roomTemperatureRecommendation }}
+
+ 香型需人工更换,音乐风格暂不能自动选择,室温仅供参考;一键启动只下发当前已支持的设备参数。
+
+
一键启动预设
+
湿度 {{ result.sceneConfig.targetHumidity }}% · 灯光 {{ result.sceneConfig.lightColorTemperatureKelvin }}K
+
香氛{{ result.sceneConfig.enableFragrance ? '开启' : '关闭' }} · 音箱睡眠模式 {{ result.sceneConfig.speakerVolumePercent }}% 音量
+
床垫加热按重庆冬季天气决定;软硬度按健康档案 BMI 给出可手动修改的演示起点。
+
+
+
+ 正在读取四诊记录…
+
+
+ {{ section.title }}
{{ section.hint }}
+
+
{{ question.title }} 必选
+
+
+
+
+
+
+
+
+
+
+
+
+ 本问卷为内部演示的固定评分规则,不等同于专业体质辨识量表,不用于诊断或治疗。
+
+
+
+
+
+