feat: 新API
This commit is contained in:
@@ -0,0 +1,125 @@
|
||||
<template>
|
||||
<v-card class="rounded-xl elevation-2 mt-4" color="surface">
|
||||
<v-card-title class="d-flex align-center justify-space-between">
|
||||
<span>11 項體適能測驗</span>
|
||||
<v-chip color="primary" size="small" variant="tonal">
|
||||
{{ completedCount }}/{{ displayItems.length }} 項有資料
|
||||
</v-chip>
|
||||
</v-card-title>
|
||||
|
||||
<v-card-subtitle> 顯示目前帳號各測驗項目的最新成績。 </v-card-subtitle>
|
||||
|
||||
<v-card-text>
|
||||
<v-progress-linear
|
||||
v-if="loading"
|
||||
class="mb-4"
|
||||
color="primary"
|
||||
indeterminate
|
||||
rounded
|
||||
/>
|
||||
|
||||
<v-alert
|
||||
v-if="error"
|
||||
class="mb-4"
|
||||
density="compact"
|
||||
type="error"
|
||||
variant="tonal"
|
||||
>
|
||||
{{ error }}
|
||||
<v-btn
|
||||
class="ml-2"
|
||||
color="error"
|
||||
size="small"
|
||||
variant="text"
|
||||
@click="$emit('retry')"
|
||||
>
|
||||
重試
|
||||
</v-btn>
|
||||
</v-alert>
|
||||
|
||||
<v-row density="comfortable">
|
||||
<v-col
|
||||
v-for="item in displayItems"
|
||||
:key="item.fitnessCode"
|
||||
cols="12"
|
||||
sm="6"
|
||||
md="4"
|
||||
>
|
||||
<div
|
||||
class="test-item d-flex align-center justify-space-between rounded-lg pa-3"
|
||||
>
|
||||
<div class="mr-3">
|
||||
<div class="text-body-2 font-weight-bold">
|
||||
{{ item.fitnessName }}
|
||||
</div>
|
||||
<div class="text-caption text-grey">{{ item.fitnessCode }}</div>
|
||||
</div>
|
||||
<div
|
||||
class="text-body-1 font-weight-black text-no-wrap"
|
||||
:class="item.fitnessScore === null ? 'text-grey' : 'text-primary'"
|
||||
>
|
||||
{{ item.fitnessScore ?? "無資料" }}
|
||||
</div>
|
||||
</div>
|
||||
</v-col>
|
||||
</v-row>
|
||||
</v-card-text>
|
||||
</v-card>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { computed } from "vue";
|
||||
import type { PhysicalFitnessItem } from "@/api/types";
|
||||
|
||||
const props = defineProps<{
|
||||
error: string;
|
||||
items: PhysicalFitnessItem[];
|
||||
loading: boolean;
|
||||
}>();
|
||||
|
||||
defineEmits<{
|
||||
retry: [];
|
||||
}>();
|
||||
|
||||
const FITNESS_TEST_DEFINITIONS = [
|
||||
{ code: "UPGO_244M", name: "八英呎起走(秒)", aliases: [] },
|
||||
{ code: "SIT_STAND_30S", name: "30 秒坐站(次)", aliases: ["SIT30S"] },
|
||||
{ code: "SIT_REACH_L", name: "坐姿體前彎左側(公分)", aliases: [] },
|
||||
{ code: "SIT_REACH_R", name: "坐姿體前彎右側(公分)", aliases: [] },
|
||||
{ code: "ARM_CURL_L", name: "30 秒手臂彎舉左側(次)", aliases: [] },
|
||||
{ code: "ARM_CURL_R", name: "30 秒手臂彎舉右側(次)", aliases: [] },
|
||||
{ code: "BACK_SCRATCH_L", name: "抓背測驗左側(公分)", aliases: [] },
|
||||
{ code: "BACK_SCRATCH_R", name: "抓背測驗右側(公分)", aliases: [] },
|
||||
{ code: "ONE_LEG_STAND_L", name: "單腳站立左側(秒)", aliases: [] },
|
||||
{ code: "ONE_LEG_STAND_R", name: "單腳站立右側(秒)", aliases: [] },
|
||||
{ code: "STEP_TEST_30S", name: "30 秒抬膝踏步(次)", aliases: [] },
|
||||
] as const;
|
||||
|
||||
const displayItems = computed(() =>
|
||||
FITNESS_TEST_DEFINITIONS.map((definition) => {
|
||||
const item = props.items.find(
|
||||
(candidate) =>
|
||||
candidate.fitnessCode === definition.code ||
|
||||
definition.aliases.some((alias) => alias === candidate.fitnessCode),
|
||||
);
|
||||
|
||||
return {
|
||||
fitnessCode: definition.code,
|
||||
fitnessName: item?.fitnessName || definition.name,
|
||||
fitnessScore: item?.fitnessScore ?? null,
|
||||
};
|
||||
}),
|
||||
);
|
||||
|
||||
const completedCount = computed(
|
||||
() => displayItems.value.filter((item) => item.fitnessScore !== null).length,
|
||||
);
|
||||
</script>
|
||||
|
||||
<style scoped>
|
||||
.test-item {
|
||||
min-height: 72px;
|
||||
border: 1px solid rgba(0, 0, 0, 0.08);
|
||||
background: rgba(var(--v-theme-primary), 0.03);
|
||||
}
|
||||
</style>
|
||||
Reference in New Issue
Block a user