feat: bmi
This commit is contained in:
@@ -0,0 +1,87 @@
|
||||
<script setup lang="ts">
|
||||
import { onMounted, shallowRef } from "vue";
|
||||
import { useRouter } from "vue-router";
|
||||
import type { BodyCompositionResponse } from "@/api/types";
|
||||
import { fetchBodyComposition } from "@/api/physical-fitness";
|
||||
import BmiAnalysisCard from "@/components/body-composition/BmiAnalysisCard.vue";
|
||||
import BodyFatAnalysisCard from "@/components/body-composition/BodyFatAnalysisCard.vue";
|
||||
import Layout from "@/components/layout/DefaultLayout.vue";
|
||||
import PageHeading from "@/components/layout/PageHeading.vue";
|
||||
import { useAppStore } from "@/stores/app";
|
||||
import { getApiErrorMessage } from "@/utils/api-error";
|
||||
|
||||
const store = useAppStore();
|
||||
const router = useRouter();
|
||||
const bodyComposition = shallowRef<BodyCompositionResponse | null>(null);
|
||||
const loading = shallowRef(false);
|
||||
const errorMessage = shallowRef("");
|
||||
|
||||
async function loadBodyComposition() {
|
||||
const userCode = store.userID.trim();
|
||||
if (!userCode) {
|
||||
await router.replace({
|
||||
name: "login",
|
||||
query: { redirect: "/body-composition" },
|
||||
});
|
||||
return;
|
||||
}
|
||||
|
||||
loading.value = true;
|
||||
errorMessage.value = "";
|
||||
|
||||
try {
|
||||
bodyComposition.value = await fetchBodyComposition(userCode);
|
||||
} catch (error) {
|
||||
errorMessage.value = getApiErrorMessage(
|
||||
error,
|
||||
"身體數值資料載入失敗,請稍後再試。",
|
||||
);
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(loadBodyComposition);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<Layout>
|
||||
<template #main>
|
||||
<div class="max-width-mobile pb-2">
|
||||
<PageHeading title="身體數值分析" />
|
||||
|
||||
<template v-if="loading">
|
||||
<v-skeleton-loader class="rounded-xl mb-4" type="article, table" />
|
||||
<v-skeleton-loader class="rounded-xl" type="article" />
|
||||
</template>
|
||||
|
||||
<v-alert
|
||||
v-else-if="errorMessage"
|
||||
class="rounded-xl"
|
||||
icon="mdi-alert-circle-outline"
|
||||
type="error"
|
||||
variant="tonal"
|
||||
>
|
||||
{{ errorMessage }}
|
||||
<template #append>
|
||||
<v-btn size="small" variant="text" @click="loadBodyComposition">
|
||||
重試
|
||||
</v-btn>
|
||||
</template>
|
||||
</v-alert>
|
||||
|
||||
<template v-else-if="bodyComposition">
|
||||
<BmiAnalysisCard
|
||||
:bmi="bodyComposition.bmi"
|
||||
:bmi-status="bodyComposition.bmiStatus"
|
||||
:weight-kg="bodyComposition.weightKg"
|
||||
/>
|
||||
<BodyFatAnalysisCard
|
||||
:body-fat-percent="bodyComposition.bodyFatPercent"
|
||||
:body-fat-range="bodyComposition.bodyFatRange"
|
||||
/>
|
||||
</template>
|
||||
</div>
|
||||
</template>
|
||||
</Layout>
|
||||
</template>
|
||||
@@ -2,19 +2,7 @@
|
||||
<Layout>
|
||||
<template #main>
|
||||
<template v-if="store.groupLoaded">
|
||||
<!-- 頁面標題:刻意縮小,把版面讓給下方的組別 -->
|
||||
<div class="d-flex align-center mb-3">
|
||||
<v-icon
|
||||
class="mr-2"
|
||||
color="primary"
|
||||
icon="mdi-calendar-week"
|
||||
size="18"
|
||||
/>
|
||||
|
||||
<span class="text-title-small font-weight-bold text-grey-darken-1"
|
||||
>課程建議</span
|
||||
>
|
||||
</div>
|
||||
<PageHeading title="課程建議" />
|
||||
|
||||
<!-- 體適能組別:本頁所有內容都依它而定,因此獨立成一張顯眼的卡片 -->
|
||||
<v-card
|
||||
@@ -401,6 +389,7 @@
|
||||
import type { TimeSlot, YesNo } from "@/api/types";
|
||||
import { computed, onMounted, ref } from "vue";
|
||||
import Layout from "@/components/layout/DefaultLayout.vue";
|
||||
import PageHeading from "@/components/layout/PageHeading.vue";
|
||||
import { EXERCISE_DURATION_RECOMMENDATION_ENABLED } from "@/config/features";
|
||||
import { COURSE_DETAIL } from "@/data/courseDetail";
|
||||
import { useCourseStore } from "@/stores/course";
|
||||
|
||||
@@ -104,6 +104,14 @@
|
||||
title="重新計算體適能評級"
|
||||
@click="openGenerateDialog"
|
||||
/>
|
||||
|
||||
<ModuleRouterCard
|
||||
description="查看體重、BMI 與體脂率的個人分析結果。"
|
||||
icon="mdi-scale-bathroom"
|
||||
icon-color="primary"
|
||||
title="身體數值分析"
|
||||
to="/body-composition"
|
||||
/>
|
||||
</div>
|
||||
|
||||
<!-- 課程建議入口 -->
|
||||
|
||||
@@ -5,6 +5,7 @@ import sportsEmpowerHubImage from "@/assets/introduction/expo_park_open/open_01.
|
||||
import fitnessResearchImage from "@/assets/introduction/expo_park_open/open_02.jpg";
|
||||
import expoParkZonesImage from "@/assets/introduction/expo_park_open/open_03.jpg";
|
||||
import Layout from "@/components/layout/DefaultLayout.vue";
|
||||
import PageHeading from "@/components/layout/PageHeading.vue";
|
||||
|
||||
const slides = [
|
||||
{
|
||||
@@ -31,14 +32,9 @@ const activeSlide = shallowRef(0);
|
||||
<template>
|
||||
<Layout>
|
||||
<template #main>
|
||||
<v-card variant="flat">
|
||||
<v-card-title
|
||||
class="font-weight-bold text-primary d-flex align-center py-0"
|
||||
>
|
||||
<v-icon class="mr-4" color="accent" icon="mdi-information-outline" />
|
||||
計畫簡介
|
||||
</v-card-title>
|
||||
<PageHeading title="計畫簡介" />
|
||||
|
||||
<v-card variant="flat">
|
||||
<div class="carousel-shell">
|
||||
<v-carousel
|
||||
v-model="activeSlide"
|
||||
|
||||
Reference in New Issue
Block a user