Files
taiheEhu/src/pages/report/index.tsx
2026-05-08 11:30:37 +08:00

196 lines
8.1 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import { ScrollView, Text, View } from "@tarojs/components";
import Taro from "@tarojs/taro";
import type { CSSProperties } from "react";
import { useMemo, useState } from "react";
import { ReportChartCard } from "../../components/report/chart-card";
import { ReportChipGroup } from "../../components/report/chip-group";
import { ReportDaytimePrediction } from "../../components/report/daytime-prediction";
import { ReportDistributionCard } from "../../components/report/distribution-card";
import { ReportInsightList } from "../../components/report/insight-list";
import { ReportKvList } from "../../components/report/kv-list";
import { ReportMetricGrid } from "../../components/report/metric-grid";
import { ReportPageHeader } from "../../components/report/page-header";
import { ReportScoreOverview } from "../../components/report/score-overview";
import { ReportSummaryBlock } from "../../components/report/summary-block";
import { reportDimensions, reportOptions, reportRecords } from "./mock";
import { getSleepLevel, pickReportRecord } from "./report-utils";
import type { ReportDimension } from "./types";
import "./index.scss";
function getDefaultSelection(dimension: ReportDimension) {
const options = reportOptions[dimension];
return {
dateKey: options.dates[0].value,
roomKey: options.rooms[0].value,
deviceKey: options.devices[0].value
};
}
export default function ReportPage() {
const [dimension, setDimension] = useState<ReportDimension>("daily");
const initialSelection = getDefaultSelection("daily");
const [dateKey, setDateKey] = useState(initialSelection.dateKey);
const [roomKey, setRoomKey] = useState(initialSelection.roomKey);
const [deviceKey, setDeviceKey] = useState(initialSelection.deviceKey);
const currentOptions = reportOptions[dimension];
const currentRecord = useMemo(
() => pickReportRecord(reportRecords[dimension], dateKey, roomKey, deviceKey),
[dateKey, deviceKey, dimension, roomKey]
);
const sleepLevel = getSleepLevel(currentRecord.score);
const [selectedSleepTrend, setSelectedSleepTrend] = useState(currentRecord.sleepTrend[0].id);
const [selectedScoreTrend, setSelectedScoreTrend] = useState(currentRecord.scoreTrend[0].id);
const [selectedHeartRatePoint, setSelectedHeartRatePoint] = useState(currentRecord.heartRatePoints[0].id);
const [selectedBreathingPoint, setSelectedBreathingPoint] = useState(currentRecord.breathingPoints[0].id);
const [selectedEventPoint, setSelectedEventPoint] = useState(currentRecord.eventPoints[0].id);
const resetSelections = (nextDimension: ReportDimension, nextDate: string, nextRoom: string, nextDevice: string) => {
const record = pickReportRecord(reportRecords[nextDimension], nextDate, nextRoom, nextDevice);
setSelectedSleepTrend(record.sleepTrend[0].id);
setSelectedScoreTrend(record.scoreTrend[0].id);
setSelectedHeartRatePoint(record.heartRatePoints[0].id);
setSelectedBreathingPoint(record.breathingPoints[0].id);
setSelectedEventPoint(record.eventPoints[0].id);
};
const handleDimensionChange = (nextValue: string) => {
const nextDimension = nextValue as ReportDimension;
const nextSelection = getDefaultSelection(nextDimension);
setDimension(nextDimension);
setDateKey(nextSelection.dateKey);
setRoomKey(nextSelection.roomKey);
setDeviceKey(nextSelection.deviceKey);
resetSelections(nextDimension, nextSelection.dateKey, nextSelection.roomKey, nextSelection.deviceKey);
};
const handleDateChange = (nextDate: string) => {
setDateKey(nextDate);
resetSelections(dimension, nextDate, roomKey, deviceKey);
};
const handleRoomChange = (nextRoom: string) => {
setRoomKey(nextRoom);
resetSelections(dimension, dateKey, nextRoom, deviceKey);
};
const handleDeviceChange = (nextDevice: string) => {
setDeviceKey(nextDevice);
resetSelections(dimension, dateKey, roomKey, nextDevice);
};
const pageStyle = {
"--report-top-gap": `${(typeof Taro.getWindowInfo === "function" ? Taro.getWindowInfo().statusBarHeight : Taro.getSystemInfoSync().statusBarHeight) || 0}px`
} as CSSProperties;
return (
<ScrollView className="report-page" scrollY style={pageStyle}>
<View className="report-page__glow report-page__glow--one" />
<View className="report-page__glow report-page__glow--two" />
<View className="report-page__content">
<ReportPageHeader
title={currentRecord.babyName}
subtitle={currentRecord.dateLabel}
onBack={() => Taro.navigateBack({ delta: 1 })}
onShare={() =>
Taro.showToast({
title: "分享功能待接入",
icon: "none"
})
}
/>
<View className="report-toolbar">
<ReportChipGroup options={reportDimensions} value={dimension} onChange={handleDimensionChange} />
<View className="report-filters">
<View className="report-filter">
<Text className="report-filter__label"></Text>
<ReportChipGroup options={currentOptions.dates} value={dateKey} onChange={handleDateChange} compact />
</View>
<View className="report-filter">
<Text className="report-filter__label"></Text>
<ReportChipGroup options={currentOptions.rooms} value={roomKey} onChange={handleRoomChange} compact />
</View>
<View className="report-filter">
<Text className="report-filter__label"></Text>
<ReportChipGroup options={currentOptions.devices} value={deviceKey} onChange={handleDeviceChange} compact />
</View>
</View>
</View>
<ReportScoreOverview
score={currentRecord.score}
levelLabel={sleepLevel.label}
levelTone={sleepLevel.tone}
qualityStatus={currentRecord.qualityStatus}
totalSleep={currentRecord.totalSleep}
asleepAt={currentRecord.asleepAt}
wakeAt={currentRecord.wakeAt}
factors={currentRecord.scoreFactors}
/>
<ReportChartCard
title="睡眠质量趋势"
subtitle="整夜睡眠阶段与呼吸波动"
points={currentRecord.sleepTrend}
selectedId={selectedSleepTrend}
onSelect={setSelectedSleepTrend}
/>
<ReportMetricGrid metrics={currentRecord.metrics} />
<ReportInsightList title="AI 睡眠分析" items={currentRecord.aiSummary} />
<ReportInsightList title="改善建议" items={currentRecord.suggestions} />
<ReportChartCard
title="睡眠分数趋势"
subtitle="点击节点查看阶段评分"
points={currentRecord.scoreTrend}
selectedId={selectedScoreTrend}
onSelect={setSelectedScoreTrend}
/>
<ReportSummaryBlock block={currentRecord.anomalyStats} />
<ReportKvList title="心率变异性HRV" items={currentRecord.hrvMetrics} />
<ReportChartCard
title="心率散点图"
subtitle="横轴时间,纵轴心率值"
points={currentRecord.heartRatePoints}
selectedId={selectedHeartRatePoint}
onSelect={setSelectedHeartRatePoint}
mode="scatter"
/>
<ReportChartCard
title="呼吸波形图"
subtitle="呼吸波动与暂停标记"
points={currentRecord.breathingPoints}
selectedId={selectedBreathingPoint}
onSelect={setSelectedBreathingPoint}
/>
<ReportSummaryBlock block={currentRecord.oxygenSummary} />
<ReportSummaryBlock block={currentRecord.snoreSummary} />
<ReportChartCard
title="异常事件散点图"
subtitle="呼吸暂停、心率异常、哭闹、离床"
points={currentRecord.eventPoints}
selectedId={selectedEventPoint}
onSelect={setSelectedEventPoint}
mode="scatter"
/>
<ReportDistributionCard title="睡眠结构" items={currentRecord.sleepStructure} />
<ReportKvList title="自主神经分析" items={currentRecord.autonomicMetrics} />
<ReportDaytimePrediction items={currentRecord.daytimePrediction} />
</View>
</ScrollView>
);
}