feat: initialize Evergreen Health mobile app

This commit is contained in:
fuenmao
2026-09-21 15:53:02 +08:00
commit 93e9bb604d
18 changed files with 2715 additions and 0 deletions
+20
View File
@@ -0,0 +1,20 @@
const ACCESS_TOKEN_KEY = 'evergreen-health-access-token'
const REFRESH_TOKEN_KEY = 'evergreen-health-refresh-token'
export function getToken() {
return localStorage.getItem(ACCESS_TOKEN_KEY)
}
export function hasToken() {
return Boolean(getToken())
}
export function saveTokens(accessToken: string, refreshToken: string) {
localStorage.setItem(ACCESS_TOKEN_KEY, accessToken)
localStorage.setItem(REFRESH_TOKEN_KEY, refreshToken)
}
export function clearTokens() {
localStorage.removeItem(ACCESS_TOKEN_KEY)
localStorage.removeItem(REFRESH_TOKEN_KEY)
}
+61
View File
@@ -0,0 +1,61 @@
import http from './http'
export type HealthUser = {
id: number
phone: string
displayName: string
avatarUrl?: string
}
export type AuthOutput = {
accessToken: string
refreshToken: string
user: HealthUser
}
export type HealthProfile = {
birthday?: string
gender?: string
heightCm?: number
weightKg?: number
averageSleepHours?: number
dailyExerciseMinutes?: number
dietPreference?: string
healthGoal?: string
}
export type PlanItem = {
moduleCode: string
title: string
target: string
advice: string
}
export type HealthPlan = {
id: number
version: number
title: string
summary: string
createTime: string
items: PlanItem[]
}
export type Dashboard = {
user: HealthUser
profile?: HealthProfile
selectedModules: string[]
currentPlan?: HealthPlan
}
export const healthApi = {
register: (payload: { phone: string; password: string; displayName: string }) =>
http.post<never, AuthOutput>('/api/health/auth/register', payload),
login: (payload: { phone: string; password: string }) =>
http.post<never, AuthOutput>('/api/health/auth/login', payload),
dashboard: () => http.get<never, Dashboard>('/api/health/dashboard'),
saveProfile: (payload: HealthProfile) =>
http.put<never, HealthProfile>('/api/health/profile', payload),
saveModules: (moduleCodes: string[]) =>
http.put<never, string[]>('/api/health/modules', { moduleCodes }),
generatePlan: () => http.post<never, HealthPlan>('/api/health/plans/generate')
}
+41
View File
@@ -0,0 +1,41 @@
import axios from 'axios'
import { clearTokens, getToken } from './auth'
type UnifiedResult<T> = {
type?: string
message?: string
result?: T
errors?: unknown
}
const http = axios.create({
baseURL: '/',
timeout: 15000
})
http.interceptors.request.use((config) => {
const token = getToken()
if (token) config.headers.Authorization = `Bearer ${token}`
return config
})
http.interceptors.response.use(
((response: any) => {
const body = response.data as UnifiedResult<unknown>
if (body && typeof body === 'object' && 'result' in body) {
if (body.type && body.type !== 'success') {
return Promise.reject(new Error(body.message || '请求失败'))
}
return body.result
}
return body
}) as any,
(error) => {
if (error.response?.status === 401) clearTokens()
const body = error.response?.data as UnifiedResult<unknown> | undefined
const message = body?.message || error.message || '网络请求失败'
return Promise.reject(new Error(message))
}
)
export default http