feat: 最後量測時間

This commit is contained in:
skytek_xinliang
2026-09-16 09:29:54 +08:00
parent 56dfb84890
commit 057bd335d6
4 changed files with 33 additions and 5 deletions
@@ -1,16 +1,16 @@
import type { import type {
GeneratePhysicalFitnessResponse, GeneratePhysicalFitnessResponse,
PhysicalFitnessItem, PhysicalFitnessResponse,
} from "./types"; } from "./types";
import { api } from "./client"; import { api } from "./client";
/** 取得指定使用者的 11 項最新體適能測驗成績。 */ /** 取得指定使用者的 11 項最新體適能測驗成績。 */
export function fetchPhysicalFitness( export function fetchPhysicalFitness(
userID: string, userID: string,
): Promise<PhysicalFitnessItem[]> { ): Promise<PhysicalFitnessResponse> {
return api return api
.get(`v1/physical-fitness/${encodeURIComponent(userID)}`) .get(`v1/physical-fitness/${encodeURIComponent(userID)}`)
.json<PhysicalFitnessItem[]>(); .json<PhysicalFitnessResponse>();
} }
/** 產生指定使用者的體適能評級。 */ /** 產生指定使用者的體適能評級。 */
+6
View File
@@ -12,6 +12,12 @@ export interface PhysicalFitnessItem {
fitnessScore: string | null; fitnessScore: string | null;
} }
/** PhysicalFitnessController 回傳的最新體適能測驗資料。 */
export interface PhysicalFitnessResponse {
results: PhysicalFitnessItem[];
ReceivedAt: string | null;
}
/** PhysicalFitnessController 產生評級的回應。 */ /** PhysicalFitnessController 產生評級的回應。 */
export interface GeneratePhysicalFitnessResponse { export interface GeneratePhysicalFitnessResponse {
message: string; message: string;
@@ -19,7 +19,7 @@
</div> </div>
</v-card-title> </v-card-title>
<v-card-subtitle> 顯示目前帳號各測驗項目的最新成績。 </v-card-subtitle> <v-card-subtitle> 最後量測時間:{{ formattedReceivedAt }} </v-card-subtitle>
<v-card-text> <v-card-text>
<v-progress-linear <v-progress-linear
@@ -86,6 +86,7 @@ const props = defineProps<{
error: string; error: string;
items: PhysicalFitnessItem[]; items: PhysicalFitnessItem[];
loading: boolean; loading: boolean;
receivedAt: string | null;
}>(); }>();
defineEmits<{ defineEmits<{
@@ -125,6 +126,22 @@ const displayItems = computed(() =>
const completedCount = computed( const completedCount = computed(
() => displayItems.value.filter((item) => item.fitnessScore !== null).length, () => displayItems.value.filter((item) => item.fitnessScore !== null).length,
); );
const formattedReceivedAt = computed(() => {
if (!props.receivedAt) {
return "無資料";
}
const receivedAt = new Date(props.receivedAt);
if (Number.isNaN(receivedAt.getTime())) {
return "無資料";
}
return new Intl.DateTimeFormat("zh-TW", {
dateStyle: "medium",
timeStyle: "short",
}).format(receivedAt);
});
</script> </script>
<style scoped> <style scoped>
+6 -1
View File
@@ -143,6 +143,7 @@
:error="physicalFitnessError" :error="physicalFitnessError"
:items="physicalFitnessItems" :items="physicalFitnessItems"
:loading="physicalFitnessLoading" :loading="physicalFitnessLoading"
:received-at="physicalFitnessReceivedAt"
@refresh="loadPhysicalFitness" @refresh="loadPhysicalFitness"
/> />
@@ -424,6 +425,7 @@ const generatePhysicalFitnessLoading = ref(false);
const generateResult = ref(""); const generateResult = ref("");
const generateError = ref(""); const generateError = ref("");
const physicalFitnessItems = ref<PhysicalFitnessItem[]>([]); const physicalFitnessItems = ref<PhysicalFitnessItem[]>([]);
const physicalFitnessReceivedAt = ref<string | null>(null);
const physicalFitnessLoading = ref(false); const physicalFitnessLoading = ref(false);
const physicalFitnessError = ref(""); const physicalFitnessError = ref("");
const redirectingToLogin = ref(false); const redirectingToLogin = ref(false);
@@ -507,6 +509,7 @@ function getRequiredUserID(): string | null {
async function loadPhysicalFitness() { async function loadPhysicalFitness() {
if (!store.isLoggedIn) { if (!store.isLoggedIn) {
physicalFitnessItems.value = []; physicalFitnessItems.value = [];
physicalFitnessReceivedAt.value = null;
physicalFitnessError.value = ""; physicalFitnessError.value = "";
return; return;
} }
@@ -520,7 +523,9 @@ async function loadPhysicalFitness() {
physicalFitnessError.value = ""; physicalFitnessError.value = "";
try { try {
physicalFitnessItems.value = await fetchPhysicalFitness(userID); const physicalFitnessResponse = await fetchPhysicalFitness(userID);
physicalFitnessItems.value = physicalFitnessResponse.results;
physicalFitnessReceivedAt.value = physicalFitnessResponse.ReceivedAt;
updateTodayTimeString(); updateTodayTimeString();
} catch (error) { } catch (error) {
physicalFitnessError.value = getApiErrorMessage( physicalFitnessError.value = getApiErrorMessage(