118 lines
4.8 KiB
Vue
118 lines
4.8 KiB
Vue
<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>
|