This commit is contained in:
fuenmao
2026-09-21 16:52:32 +08:00
parent 8ce6bec02d
commit 23cae3fda9
3 changed files with 123 additions and 7 deletions
+59 -5
View File
@@ -3,12 +3,14 @@ import { computed, onMounted, ref } from 'vue'
import { useRouter } from 'vue-router'
import { Button as VanButton, showToast } from 'vant'
import { clearTokens } from '../services/auth'
import { healthApi, type Dashboard } from '../services/health'
import { healthApi, type Dashboard, type HomeAssistantDevice, type HomeAssistantStatus } from '../services/health'
const router = useRouter()
const loading = ref(true)
const generating = ref(false)
const sceneRunning = ref<'sleep' | 'all-off'>()
const dashboard = ref<Dashboard>()
const homeAssistant = ref<HomeAssistantStatus>()
const moduleNames: Record<string, string> = {
sleep: '睡眠',
exercise: '运动',
@@ -20,11 +22,25 @@ const greeting = computed(() => {
if (hour < 18) return '下午好'
return '晚上好'
})
const availableDeviceCount = computed(() => homeAssistant.value?.devices.filter(item => item.isAvailable).length || 0)
function deviceState(item: HomeAssistantDevice) {
if (!item.isAvailable) return '离线'
if (item.code === 'dehumidifier' && item.value) return `${item.state === 'on' ? '运行中' : '已关闭'} · 当前湿度 ${item.value}${item.unit || ''}`
if (item.state === 'on' || item.state === 'playing') return '已开启'
if (item.state === 'off' || item.state === 'idle') return '已关闭'
return item.value ? `${item.value}${item.unit || ''}` : item.state
}
async function load() {
loading.value = true
try {
dashboard.value = await healthApi.dashboard()
const [dashboardResult, homeAssistantResult] = await Promise.all([
healthApi.dashboard(),
healthApi.homeAssistantStatus().catch(() => undefined)
])
dashboard.value = dashboardResult
homeAssistant.value = homeAssistantResult
} catch (error) {
showToast(error instanceof Error ? error.message : '加载失败')
} finally {
@@ -32,6 +48,21 @@ async function load() {
}
}
async function executeScene(scene: 'sleep' | 'all-off') {
sceneRunning.value = scene
try {
const result = scene === 'sleep'
? await healthApi.executeSleepScene()
: await healthApi.executeAllOffScene()
showToast({ type: result.success ? 'success' : 'fail', message: result.message })
homeAssistant.value = await healthApi.homeAssistantStatus()
} catch (error) {
showToast(error instanceof Error ? error.message : '场景执行失败')
} finally {
sceneRunning.value = undefined
}
}
async function generate() {
generating.value = true
try {
@@ -106,13 +137,36 @@ onMounted(load)
</section>
<section class="section-card smart-card">
<div class="section-title"><span>健康空间</span><small class="status-dot">Home Assistant 待接入</small></div>
<div class="section-title">
<span>健康空间</span>
<small class="status-dot" :class="{ online: homeAssistant?.connected }">
{{ homeAssistant?.connected ? `Home Assistant 已连接 · ${availableDeviceCount}/${homeAssistant.devices.length} 在线` : (homeAssistant?.message || '正在连接 Home Assistant') }}
</small>
</div>
<div v-if="homeAssistant?.devices.length" class="device-grid">
<article v-for="item in homeAssistant.devices" :key="item.code" class="device-pill" :class="{ offline: !item.isAvailable }">
<span></span>
<div><strong>{{ item.name }}</strong><small>{{ deviceState(item) }}</small></div>
</article>
</div>
<div class="scene-row">
<div>
<strong>舒睡模式</strong>
<p>灯光 · 空调 · 香氛 · 除湿机</p>
<p>除湿机 55% · 睡眠模式 · 低档风速 · 香氛 · 暖光</p>
</div>
<div class="scene-actions">
<button type="button" :disabled="!homeAssistant?.connected || !!sceneRunning" @click="executeScene('sleep')">
{{ sceneRunning === 'sleep' ? '执行中…' : '一键舒睡' }}
</button>
<button class="secondary" type="button" :disabled="!homeAssistant?.connected || !!sceneRunning" @click="executeScene('all-off')">
{{ sceneRunning === 'all-off' ? '执行中…' : '全部关闭' }}
</button>
</div>
</div>
<div v-if="homeAssistant?.environment.some(item => item.isAvailable)" class="environment-grid">
<div v-for="item in homeAssistant.environment.filter(item => item.isAvailable)" :key="item.code">
<small>{{ item.name }}</small><strong>{{ item.value }}{{ item.unit || '' }}</strong>
</div>
<button type="button">准备就绪</button>
</div>
</section>