import { defineStore } from 'pinia' import { computed, ref } from 'vue' export interface UserProfile { name: string birthday: string gender: string height: number weight: number phone: string email: string emergencyContact: string emergencyPhone: string consent: boolean } export interface SelfAssessment { exerciseFrequency: string painLevel: string hasFalls: boolean chronicDiseases: string[] wantedImprovements: string[] scoreLevel: string completed: boolean } export interface Appointment { id: string title: string date: string time: string location: string status: '未報名' | '已報名' | '已報到' slots: number organizer: string items: string[] } export interface MeasurementStation { name: string completed: boolean time?: string values: Record } function createDefaultUserProfile (): UserProfile { return { name: '林大明', birthday: '1961-08-15', gender: '男', height: 168, weight: 65, phone: '0912-345-678', email: 'jianguo.lin@email.com', emergencyContact: '林小明 (長子)', emergencyPhone: '0987-654-321', consent: true, } } function createDefaultSelfAssessment (): SelfAssessment { return { exerciseFrequency: '1-2', painLevel: '輕微', hasFalls: false, chronicDiseases: ['高血壓'], wantedImprovements: ['肌力', '平衡'], scoreLevel: '需追蹤', completed: true, } } function createDefaultAppointments (): Appointment[] { const measurementItems = [ '血壓', '體脂', '握力', '5次坐站', '30秒椅子坐立', '原地抬膝踏步', '手臂屈舉', '抓背測驗', '坐姿體前彎', '2.44米繞物', '開眼單足立', ] const location = '信義區運動中心 3 樓多功能教室' const organizer = '臺北市政府體育局' return [ { id: 'apt-01', title: '銀髮活力公益普測 A 時段', date: '2026-07-04', time: '09:00 - 10:00', location, status: '已報名', slots: 12, organizer, items: measurementItems, }, { id: 'apt-02', title: '銀髮活力公益普測 B 時段', date: '2026-07-04', time: '10:30 - 11:30', location, status: '已報到', slots: 5, organizer, items: measurementItems, }, { id: 'apt-03', title: '銀髮活力公益普測 C 時段', date: '2026-07-04', time: '13:30 - 14:30', location, status: '未報名', slots: 45, organizer, items: measurementItems, }, ] } function createDefaultMeasurements (): Record { return { BloodPressure: { name: '血壓量測', completed: true, time: '2026-06-18 15:00', values: { systolic: 132, diastolic: 84, heartRate: 72 }, }, BodyFat: { name: '體組成分析', completed: true, time: '2026-06-18 15:05', values: { bmi: 23, bodyFatPct: 24.5 }, }, GripStrength: { name: '左右握力', completed: false, values: { leftHand: 0, rightHand: 0 }, }, Sit5x: { name: '5次坐站測試', completed: false, values: { durationSeconds: 0 }, }, Sit30s: { name: '30秒椅子坐立', completed: false, values: { count: 0 }, }, StepTest: { name: '2分鐘原地抬膝踏步', completed: false, values: { count: 0 }, }, ArmTest: { name: '30秒肱二頭肌手臂屈舉', completed: false, values: { count: 0 }, }, BackTest: { name: '抓背測驗(柔軟度)', completed: false, values: { leftDistanceCm: 0, rightDistanceCm: 0 }, }, SitReachTest: { name: '坐姿體前彎(柔軟度)', completed: false, values: { distanceCm: 0 }, }, UpGo8ft: { name: '2.44公尺起立繞物(敏捷)', completed: false, values: { durationSeconds: 0 }, }, OneLegTest: { name: '開眼單足立(平衡)', completed: false, values: { leftSeconds: 0, rightSeconds: 0 }, }, } } function createDefaultRadarData () { return { strength: 65, balance: 60, flexibility: 55, agility: 70, cardio: 62, } } export const useAppStore = defineStore('app', () => { const isLoggedIn = ref(false) const userProfile = ref(createDefaultUserProfile()) const selfAssessment = ref(createDefaultSelfAssessment()) const appointments = ref(createDefaultAppointments()) const measurements = ref>(createDefaultMeasurements()) const healthScore = ref(72) const radarData = ref(createDefaultRadarData()) const isReportGenerated = ref(false) const completedStationsCount = computed(() => { return Object.values(measurements.value).filter(station => station.completed).length }) const totalStationsCount = computed(() => { return Object.keys(measurements.value).length }) const progressPercent = computed((): number => { const keys = Object.keys(measurements.value) if (keys.length === 0) { return 0 } const done = Object.values(measurements.value).filter(station => station.completed).length return Math.round((done / keys.length) * 100) }) function login (phone: string) { isLoggedIn.value = true userProfile.value.phone = phone } function logout () { isLoggedIn.value = false } function updateProfile (profile: Partial) { userProfile.value = { ...userProfile.value, ...profile } } function submitSelfAssessment (answers: Partial) { selfAssessment.value = { ...selfAssessment.value, ...answers, completed: true, } let level = '低風險' if (answers.exerciseFrequency === '0' || answers.hasFalls === true) { level = '需追蹤' } if ( answers.painLevel === '中度' || answers.painLevel === '重度' || (answers.chronicDiseases && answers.chronicDiseases.length >= 2) ) { level = '建議諮詢' } selfAssessment.value.scoreLevel = level } function registerAppointment (appointmentId: string) { const appointment = appointments.value.find(item => item.id === appointmentId) if (!appointment) { return } appointment.status = '已報名' appointment.slots -= 1 } function cancelAppointment (appointmentId: string) { const appointment = appointments.value.find(item => item.id === appointmentId) if (!appointment) { return } appointment.status = '未報名' appointment.slots += 1 } function checkinAppointment (appointmentId: string) { const appointment = appointments.value.find(item => item.id === appointmentId) if (!appointment) { return } appointment.status = '已報到' } function saveMeasurement (code: string, values: Record) { const measurement = measurements.value[code] if (!measurement) { return } const now = new Date() const timeStr = `${now.getFullYear()}-${String(now.getMonth() + 1).padStart(2, '0')}-${String(now.getDate()).padStart(2, '0')} ${String(now.getHours()).padStart(2, '0')}:${String(now.getMinutes()).padStart(2, '0')}` measurement.values = { ...values } measurement.completed = true measurement.time = timeStr } function generateFinalReport () { const currentMeasurements = measurements.value let strScore = 65 if (currentMeasurements.GripStrength.completed) { const avgGrip = ((currentMeasurements.GripStrength.values.leftHand || 0) + (currentMeasurements.GripStrength.values.rightHand || 0)) / 2 const limit = userProfile.value.gender === '男' ? 30 : 20 strScore = Math.min(95, Math.max(50, Math.round((avgGrip / limit) * 75))) } let balScore = 60 if (currentMeasurements.OneLegTest.completed) { const maxSec = Math.max( currentMeasurements.OneLegTest.values.leftSeconds || 0, currentMeasurements.OneLegTest.values.rightSeconds || 0, ) balScore = Math.min(98, Math.max(40, Math.round((maxSec / 30) * 90))) } let flexScore = 55 if (currentMeasurements.SitReachTest.completed) { const dist = currentMeasurements.SitReachTest.values.distanceCm || 0 flexScore = Math.min(95, Math.max(45, 60 + dist * 1.5)) } let agiScore = 70 if (currentMeasurements.UpGo8ft.completed) { const sec = currentMeasurements.UpGo8ft.values.durationSeconds || 10 agiScore = Math.min(95, Math.max(40, Math.round(100 - sec * 5))) } let cardScore = 62 if (currentMeasurements.StepTest.completed) { const count = currentMeasurements.StepTest.values.count || 60 cardScore = Math.min(95, Math.max(45, Math.round((count / 80) * 80))) } radarData.value = { strength: strScore, balance: balScore, flexibility: flexScore, agility: agiScore, cardio: cardScore, } const avg = Math.round((strScore + balScore + flexScore + agiScore + cardScore) / 5) healthScore.value = Math.min(100, avg + 5) isReportGenerated.value = true } function resetMeasurements () { for (const [key, measurement] of Object.entries(measurements.value)) { if (key !== 'BloodPressure' && key !== 'BodyFat') { measurement.completed = false for (const valueKey of Object.keys(measurement.values)) { measurement.values[valueKey] = 0 } } } isReportGenerated.value = false healthScore.value = 72 radarData.value = createDefaultRadarData() } return { isLoggedIn, userProfile, selfAssessment, appointments, measurements, healthScore, radarData, isReportGenerated, completedStationsCount, totalStationsCount, progressPercent, login, logout, updateProfile, submitSelfAssessment, registerAppointment, cancelAppointment, checkinAppointment, saveMeasurement, generateFinalReport, resetMeasurements, } })