feat: bmi
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
import type {
|
||||
BodyCompositionResponse,
|
||||
GeneratePhysicalFitnessResponse,
|
||||
PhysicalFitnessResponse,
|
||||
} from "./types";
|
||||
@@ -21,3 +22,12 @@ export function generatePhysicalFitness(
|
||||
.post(`v1/physical-fitness/generate/${encodeURIComponent(userID)}`)
|
||||
.json<GeneratePhysicalFitnessResponse>();
|
||||
}
|
||||
|
||||
/** 取得指定使用者的身體組成分析。 */
|
||||
export function fetchBodyComposition(
|
||||
userCode: string,
|
||||
): Promise<BodyCompositionResponse> {
|
||||
return api
|
||||
.get(`v1/physical-fitness/body-composition/${encodeURIComponent(userCode)}`)
|
||||
.json<BodyCompositionResponse>();
|
||||
}
|
||||
|
||||
@@ -28,6 +28,16 @@ export interface GeneratePhysicalFitnessResponse {
|
||||
message: string;
|
||||
}
|
||||
|
||||
/** PhysicalFitnessController 回傳的身體組成分析。 */
|
||||
export interface BodyCompositionResponse {
|
||||
heightCm: number;
|
||||
weightKg: number;
|
||||
bmi: number;
|
||||
bmiStatus: string;
|
||||
bodyFatPercent: number;
|
||||
bodyFatRange: string;
|
||||
}
|
||||
|
||||
/** 性別 */
|
||||
export type Gender = "male" | "female";
|
||||
|
||||
|
||||
@@ -0,0 +1,206 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import { WHO_BMI_STANDARDS } from "@/data/whoBmiStandards";
|
||||
import {
|
||||
BODY_COMPOSITION_COLORS,
|
||||
getBmiTone,
|
||||
} from "@/utils/body-composition-status";
|
||||
|
||||
const props = defineProps<{
|
||||
weightKg: number;
|
||||
bmi: number;
|
||||
bmiStatus: string;
|
||||
}>();
|
||||
|
||||
const statusColor = computed(
|
||||
() => BODY_COMPOSITION_COLORS[getBmiTone(props.bmi)],
|
||||
);
|
||||
|
||||
const recommendation = computed(() => {
|
||||
if (props.bmi < 18.5) {
|
||||
return "體重低於正常範圍,建議留意營養攝取與健康狀況。";
|
||||
}
|
||||
|
||||
if (props.bmi < 25) {
|
||||
return "體重位於正常範圍,請持續維持均衡飲食與規律運動。";
|
||||
}
|
||||
|
||||
if (props.bmi < 30) {
|
||||
return "體重已超過正常範圍,建議適度調整體重。";
|
||||
}
|
||||
|
||||
return "您的 BMI 已進入肥胖範圍,建議諮詢專業人員並逐步調整生活習慣。";
|
||||
});
|
||||
|
||||
function formatMeasurement(value: number): string {
|
||||
return Number.isFinite(value) ? value.toFixed(1) : "--";
|
||||
}
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-card class="analysis-card rounded-xl" elevation="1">
|
||||
<v-card-title class="text-h5 font-weight-black text-center pt-6">
|
||||
體重與 BMI 分析
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text class="pa-4 pa-sm-6">
|
||||
<div class="metrics-grid mb-5">
|
||||
<div class="metric-cell text-center">
|
||||
<div class="text-body-large text-grey-darken-2 mb-2">體重</div>
|
||||
<div class="metric-value text-grey-darken-4">
|
||||
{{ formatMeasurement(weightKg) }}
|
||||
<span class="metric-unit">公斤</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="metric-cell text-center">
|
||||
<div class="text-body-large text-grey-darken-2 mb-2">BMI</div>
|
||||
<div class="metric-value" :style="{ color: statusColor }">
|
||||
{{ formatMeasurement(bmi) }}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-wrap justify-center align-center ga-2 mb-5">
|
||||
<span class="text-title-medium">您的 BMI 屬於</span>
|
||||
<v-chip
|
||||
class="semantic-chip font-weight-black"
|
||||
:color="statusColor"
|
||||
label
|
||||
size="large"
|
||||
variant="flat"
|
||||
>
|
||||
{{ bmiStatus }}
|
||||
</v-chip>
|
||||
</div>
|
||||
|
||||
<v-alert
|
||||
class="recommendation-alert mb-6"
|
||||
:color="statusColor"
|
||||
icon="mdi-bullhorn-outline"
|
||||
variant="tonal"
|
||||
>
|
||||
{{ recommendation }}
|
||||
</v-alert>
|
||||
|
||||
<v-sheet class="who-panel rounded-xl pa-4" color="grey-lighten-4">
|
||||
<div class="who-title text-center mb-1">世界衛生組織 WHO 定義</div>
|
||||
<div class="who-formula text-grey-darken-2 text-center mb-5">
|
||||
(BMI = 體重 ÷ 身高²)
|
||||
</div>
|
||||
|
||||
<div class="who-standards">
|
||||
<div
|
||||
v-for="standard in WHO_BMI_STANDARDS"
|
||||
:key="standard.label"
|
||||
class="who-standard rounded-lg text-center"
|
||||
:style="{
|
||||
backgroundColor: BODY_COMPOSITION_COLORS[standard.tone],
|
||||
}"
|
||||
>
|
||||
<div class="who-standard-label">{{ standard.label }}</div>
|
||||
<div class="who-standard-range">{{ standard.range }}</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<p class="who-note text-grey-darken-2 text-center mb-0 mt-5">
|
||||
BMI 只是初步判斷,需搭配體脂率<br
|
||||
class="d-none d-sm-inline"
|
||||
/>與肌肉量進一步分析。
|
||||
</p>
|
||||
</v-sheet>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.analysis-card {
|
||||
border: 1px solid rgba(var(--v-theme-primary), 0.24);
|
||||
}
|
||||
|
||||
.metrics-grid {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(2, minmax(0, 1fr));
|
||||
}
|
||||
|
||||
.metric-cell {
|
||||
min-width: 0;
|
||||
padding: 8px 12px;
|
||||
}
|
||||
|
||||
.metric-cell + .metric-cell {
|
||||
border-left: 1px solid rgba(0, 0, 0, 0.14);
|
||||
}
|
||||
|
||||
.metric-value {
|
||||
font-size: clamp(2.25rem, 10vw, 3.125rem);
|
||||
font-weight: 900;
|
||||
line-height: 1.1;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.metric-unit {
|
||||
font-size: clamp(0.875rem, 3.5vw, 1.25rem);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.recommendation-alert {
|
||||
font-size: 1rem;
|
||||
line-height: 1.6;
|
||||
}
|
||||
|
||||
.semantic-chip {
|
||||
color: #252525 !important;
|
||||
}
|
||||
|
||||
.who-title {
|
||||
font-size: clamp(1.25rem, 5vw, 1.65rem);
|
||||
font-weight: 700;
|
||||
line-height: 1.35;
|
||||
}
|
||||
|
||||
.who-formula,
|
||||
.who-note {
|
||||
font-size: clamp(1rem, 4vw, 1.25rem);
|
||||
line-height: 1.55;
|
||||
}
|
||||
|
||||
.who-standards {
|
||||
display: grid;
|
||||
grid-template-columns: repeat(3, minmax(0, 1fr));
|
||||
gap: 8px;
|
||||
}
|
||||
|
||||
.who-standard {
|
||||
min-width: 0;
|
||||
padding: 18px 6px 16px;
|
||||
color: #2b2b2b;
|
||||
}
|
||||
|
||||
.who-standard-label {
|
||||
margin-bottom: 4px;
|
||||
font-size: clamp(1.2rem, 5vw, 1.55rem);
|
||||
font-weight: 500;
|
||||
}
|
||||
|
||||
.who-standard-range {
|
||||
font-size: clamp(0.92rem, 3.8vw, 1.12rem);
|
||||
font-weight: 800;
|
||||
line-height: 1.25;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
@media (max-width: 420px) {
|
||||
.metric-cell {
|
||||
padding-inline: 6px;
|
||||
}
|
||||
|
||||
.who-panel {
|
||||
padding-inline: 12px !important;
|
||||
}
|
||||
|
||||
.who-standards {
|
||||
gap: 6px;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -0,0 +1,73 @@
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import {
|
||||
BODY_COMPOSITION_COLORS,
|
||||
getBodyFatTone,
|
||||
} from "@/utils/body-composition-status";
|
||||
|
||||
const props = defineProps<{
|
||||
bodyFatPercent: number;
|
||||
bodyFatRange: string;
|
||||
}>();
|
||||
|
||||
const statusColor = computed(
|
||||
() => BODY_COMPOSITION_COLORS[getBodyFatTone(props.bodyFatRange)],
|
||||
);
|
||||
|
||||
const formattedBodyFatPercent = computed(() =>
|
||||
Number.isFinite(props.bodyFatPercent)
|
||||
? props.bodyFatPercent.toFixed(1)
|
||||
: "--",
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<v-card class="analysis-card rounded-xl mt-4" elevation="1">
|
||||
<v-card-title class="text-h5 font-weight-black text-center pt-6">
|
||||
體脂率分析
|
||||
</v-card-title>
|
||||
|
||||
<v-card-text class="text-center pa-6">
|
||||
<div class="text-title-medium text-grey-darken-2 mb-2">體脂率</div>
|
||||
<div class="body-fat-value mb-6" :style="{ color: statusColor }">
|
||||
{{ formattedBodyFatPercent }}<span class="body-fat-unit">%</span>
|
||||
</div>
|
||||
|
||||
<div class="d-flex flex-wrap justify-center align-center ga-2">
|
||||
<span class="text-title-medium">您的體脂率屬於</span>
|
||||
<v-chip
|
||||
class="semantic-chip font-weight-black"
|
||||
:color="statusColor"
|
||||
label
|
||||
size="large"
|
||||
variant="flat"
|
||||
>
|
||||
{{ bodyFatRange }}
|
||||
</v-chip>
|
||||
<span class="text-title-medium">範圍</span>
|
||||
</div>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.analysis-card {
|
||||
border: 1px solid rgba(var(--v-theme-primary), 0.24);
|
||||
}
|
||||
|
||||
.body-fat-value {
|
||||
font-size: clamp(2.25rem, 10vw, 3.125rem);
|
||||
font-weight: 900;
|
||||
line-height: 1;
|
||||
}
|
||||
|
||||
.semantic-chip {
|
||||
color: #252525 !important;
|
||||
}
|
||||
|
||||
.body-fat-unit {
|
||||
margin-left: 6px;
|
||||
font-size: clamp(1.25rem, 5vw, 2rem);
|
||||
font-weight: 500;
|
||||
}
|
||||
</style>
|
||||
@@ -109,6 +109,15 @@
|
||||
@click="drawer = false"
|
||||
/>
|
||||
|
||||
<v-list-item
|
||||
v-if="store.isLoggedIn"
|
||||
color="primary"
|
||||
prepend-icon="mdi-scale-bathroom"
|
||||
title="身體數值分析"
|
||||
to="/body-composition"
|
||||
@click="drawer = false"
|
||||
/>
|
||||
|
||||
<v-list-item
|
||||
color="primary"
|
||||
prepend-icon="mdi-information-outline"
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
<script setup lang="ts">
|
||||
withDefaults(
|
||||
defineProps<{
|
||||
title: string;
|
||||
backTo?: string;
|
||||
}>(),
|
||||
{
|
||||
backTo: "/",
|
||||
},
|
||||
);
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<header class="page-heading d-flex align-center mb-2">
|
||||
<v-btn
|
||||
aria-label="返回首頁"
|
||||
class="flex-shrink-0"
|
||||
color="primary"
|
||||
prepend-icon="mdi-arrow-left"
|
||||
rounded="pill"
|
||||
:to="backTo"
|
||||
variant="outlined"
|
||||
>
|
||||
返回
|
||||
</v-btn>
|
||||
<h1 class="page-title text-primary">{{ title }}</h1>
|
||||
</header>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.page-heading {
|
||||
position: relative;
|
||||
min-height: 44px;
|
||||
}
|
||||
|
||||
.page-title {
|
||||
position: absolute;
|
||||
left: 50%;
|
||||
width: min(60%, 320px);
|
||||
margin: 0;
|
||||
font-size: clamp(1.25rem, 5vw, 1.75rem);
|
||||
font-weight: 900;
|
||||
line-height: 1.2;
|
||||
text-align: center;
|
||||
transform: translateX(-50%);
|
||||
}
|
||||
|
||||
@media (max-width: 420px) {
|
||||
.page-title {
|
||||
position: static;
|
||||
width: auto;
|
||||
margin-left: 12px;
|
||||
transform: none;
|
||||
}
|
||||
}
|
||||
</style>
|
||||
@@ -1,6 +1,6 @@
|
||||
export const APPOINTMENT_ENABLED = false
|
||||
export const BOTTOM_NAVIGATION_ENABLED = false
|
||||
export const CHECKIN_ENABLED = false
|
||||
export const EXERCISE_DURATION_RECOMMENDATION_ENABLED = false
|
||||
export const RADAR_ENABLED = false
|
||||
export const SELF_ASSESSMENT_ENABLED = false
|
||||
export const APPOINTMENT_ENABLED = false;
|
||||
export const BOTTOM_NAVIGATION_ENABLED = false;
|
||||
export const CHECKIN_ENABLED = false;
|
||||
export const EXERCISE_DURATION_RECOMMENDATION_ENABLED = false;
|
||||
export const RADAR_ENABLED = false;
|
||||
export const SELF_ASSESSMENT_ENABLED = false;
|
||||
|
||||
@@ -0,0 +1,17 @@
|
||||
[
|
||||
{
|
||||
"label": "正常",
|
||||
"range": "18.5–24.9",
|
||||
"tone": "normal"
|
||||
},
|
||||
{
|
||||
"label": "過重",
|
||||
"range": "25.0–29.9",
|
||||
"tone": "caution"
|
||||
},
|
||||
{
|
||||
"label": "肥胖",
|
||||
"range": "30.0以上",
|
||||
"tone": "danger"
|
||||
}
|
||||
]
|
||||
@@ -0,0 +1,10 @@
|
||||
import whoBmiStandards from "./who-bmi-standards.json";
|
||||
import type { BodyCompositionTone } from "@/utils/body-composition-status";
|
||||
|
||||
export interface WhoBmiStandard {
|
||||
label: string;
|
||||
range: string;
|
||||
tone: BodyCompositionTone;
|
||||
}
|
||||
|
||||
export const WHO_BMI_STANDARDS = whoBmiStandards as WhoBmiStandard[];
|
||||
@@ -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"
|
||||
|
||||
@@ -1,5 +1,6 @@
|
||||
import { createRouter, createWebHistory } from "vue-router";
|
||||
import Appointment from "@/pages/appointment.vue";
|
||||
import BodyComposition from "@/pages/body-composition.vue";
|
||||
import Checkin from "@/pages/checkin.vue";
|
||||
import CheckinDetail from "@/pages/checkinDetail.vue";
|
||||
import Course from "@/pages/course.vue";
|
||||
@@ -53,6 +54,16 @@ const router = createRouter({
|
||||
component: Profile,
|
||||
meta: { requiresAuth: true, title: "個人資料修改|運動玩轉健康力" },
|
||||
},
|
||||
{
|
||||
path: "/body-composition",
|
||||
name: "body-composition",
|
||||
component: BodyComposition,
|
||||
meta: {
|
||||
requiresAuth: true,
|
||||
requiresUserID: true,
|
||||
title: "身體數值分析|運動玩轉健康力",
|
||||
},
|
||||
},
|
||||
{
|
||||
path: "/appointment",
|
||||
name: "appointment",
|
||||
@@ -106,6 +117,13 @@ router.beforeEach(async (to) => {
|
||||
};
|
||||
}
|
||||
|
||||
if (to.meta.requiresUserID && !store.userID.trim()) {
|
||||
return {
|
||||
name: "login",
|
||||
query: { redirect: to.fullPath },
|
||||
};
|
||||
}
|
||||
|
||||
if (to.meta.requiresFitnessGroup) {
|
||||
const courseStore = useCourseStore();
|
||||
|
||||
|
||||
@@ -0,0 +1,19 @@
|
||||
export type BodyCompositionTone = "normal" | "caution" | "danger";
|
||||
|
||||
export const BODY_COMPOSITION_COLORS: Record<BodyCompositionTone, string> = {
|
||||
normal: "#9ac36d",
|
||||
caution: "#fbd051",
|
||||
danger: "#ef6b6b",
|
||||
};
|
||||
|
||||
export function getBmiTone(bmi: number): BodyCompositionTone {
|
||||
if (bmi >= 18.5 && bmi < 25) return "normal";
|
||||
if (bmi >= 30) return "danger";
|
||||
return "caution";
|
||||
}
|
||||
|
||||
export function getBodyFatTone(bodyFatRange: string): BodyCompositionTone {
|
||||
if (/肥胖|過高|高危|危險/.test(bodyFatRange)) return "danger";
|
||||
if (/標準|健康|正常/.test(bodyFatRange)) return "normal";
|
||||
return "caution";
|
||||
}
|
||||
Reference in New Issue
Block a user