Files
Evergreen-Health-Mobile/src/views/AuthView.vue
T
2026-10-09 16:46:35 +08:00

118 lines
4.8 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import { computed, reactive, ref } from 'vue'
import { useRouter } from 'vue-router'
import { Button as VanButton, Field as VanField, Form as VanForm, showToast } from 'vant'
import { healthApi } from '../services/health'
import { saveTokens } from '../services/auth'
import { usePageRequest } from '../composables/usePageRequest'
const router = useRouter()
const mode = ref<'login' | 'register'>('login')
const loading = ref(false)
const form = reactive({ phone: '', password: '', displayName: '' })
const title = computed(() => mode.value === 'login' ? '欢迎回来' : '开启专属健康档案')
const pageRequest = usePageRequest()
async function submit() {
if (loading.value) return
const requestMode = mode.value
const phone = form.phone.trim(), displayName = form.displayName.trim()
if (!/^1[3-9]\d{9}$/.test(phone)) { showToast('请输入正确的手机号'); return }
if (!form.password || (requestMode === 'register' && form.password.length < 8)) { showToast(requestMode === 'register' ? '注册密码至少 8 位' : '请输入密码'); return }
if (requestMode === 'register' && !displayName) { showToast('请输入姓名或称呼'); return }
const requestId = pageRequest.begin()
loading.value = true
try {
const output = requestMode === 'login'
? await healthApi.login({ phone, password: form.password })
: await healthApi.register({ phone, password: form.password, displayName })
if (!pageRequest.isCurrent(requestId)) return
saveTokens(output.accessToken, output.refreshToken)
showToast({ type: 'success', message: requestMode === 'login' ? '登录成功' : '注册成功' })
await router.replace(requestMode === 'login' ? '/dashboard' : '/tcm-assessment')
} catch (error) {
if (pageRequest.isCurrent(requestId)) showToast(error instanceof Error ? error.message : '操作失败')
} finally {
if (pageRequest.isCurrent(requestId)) loading.value = false
}
}
</script>
<template>
<main class="auth-page">
<section class="brand-panel">
<div class="brand-badge">
<span class="brand-badge-icon">常</span>
<span>一人一方 · 康养管理</span>
</div>
<p class="eyebrow">EVERGREEN HEALTH</p>
<h1>一人一方<br />健康与空间协同</h1>
<p class="brand-copy">以睡眠为切入点,融合五感空间与动态健康方案,开启日常健康管理。</p>
<div class="feature-pill-row">
<span>体质评估</span><span>舒睡联动</span><span>健康档案</span>
</div>
</section>
<section class="auth-card">
<div class="auth-tab-switch">
<button
type="button"
:class="{ active: mode === 'login' }"
:aria-pressed="mode === 'login'" :disabled="loading"
@click="mode = 'login'"
>
账号登录
</button>
<button
type="button"
:class="{ active: mode === 'register' }"
:aria-pressed="mode === 'register'" :disabled="loading"
@click="mode = 'register'"
>
注册建档
</button>
</div>
<div class="auth-heading">
<h2>{{ title }}</h2>
<p>
{{ mode === 'login' ? '登录后查看今日健康方案与空间联动' : '第一步,填写信息创建您的专属健康档案' }}
</p>
</div>
<van-form @submit="submit">
<van-field
v-if="mode === 'register'"
v-model="form.displayName"
label="姓名"
:disabled="loading" autocomplete="name" maxlength="30"
placeholder="请输入姓名或称呼"
:rules="[{ required: true, message: '请输入姓名' }]"
/>
<van-field
v-model="form.phone"
type="tel"
:disabled="loading" autocomplete="username" inputmode="tel"
label="手机号"
maxlength="11"
placeholder="请输入 11 位手机号"
:rules="[{ pattern: /^1[3-9]\d{9}$/, message: '请输入正确的手机号' }]"
/>
<van-field
v-model="form.password"
type="password"
:disabled="loading" :autocomplete="mode === 'register' ? 'new-password' : 'current-password'"
label="密码"
:placeholder="mode === 'register' ? '设置至少 8 位密码' : '请输入密码'"
:rules="[{ required: true, message: '请输入密码' }, { validator: (value: string) => mode === 'login' || value.length >= 8, message: '注册密码至少 8 位' }]"
/>
<van-button block round color="#0c5045" native-type="submit" :loading="loading" :disabled="loading">
{{ mode === 'login' ? '登 录' : '创建档案并继续' }}
</van-button>
</van-form>
<p class="privacy-note">🔒 数据安全说明:所有健康数据仅用于为您生成生活方式干预建议。</p>
</section>
</main>
</template>