healthapp
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

6.5 KiB

03-登录页面(原型)

目标

实现 APP 端登录页面原型,使用模拟数据验证登录。


UI 设计参考

参考设计稿:files/ui/登录页.png

页面布局

区域 设计要点
顶部 绿色渐变背景 (#10B981 → #2EC4B6) + 医疗插图
Logo "AI健康助手" 标题(白色 32px)+ slogan
表单卡片 白色背景,圆角 16px
输入框 圆角 12px,左侧带图标
主按钮 绿色 #10B981,圆角 24px,高度 48px

前置要求

  • 导航配置完成
  • 模拟数据服务已创建

实施步骤

步骤 1:创建登录页面

创建 src/screens/auth/LoginScreen.tsx

import React, { useState } from 'react'
import {
  View,
  Text,
  StyleSheet,
  KeyboardAvoidingView,
  Platform,
  ScrollView,
  Alert,
  Image,
} from 'react-native'
import { TextInput, Button } from 'react-native-paper'
import { useAuthStore } from '../../stores/useAuthStore'
import { mockLogin } from '../../mock/user'

const LoginScreen = () => {
  const { login } = useAuthStore()

  const [phone, setPhone] = useState('13800138000')  // 预填测试账号
  const [code, setCode] = useState('')
  const [loading, setLoading] = useState(false)
  const [countdown, setCountdown] = useState(0)

  // 模拟发送验证码
  const handleSendCode = () => {
    if (!phone.trim() || phone.length !== 11) {
      Alert.alert('提示', '请输入正确的手机号')
      return
    }
    
    // 开始倒计时
    setCountdown(60)
    const timer = setInterval(() => {
      setCountdown((prev) => {
        if (prev <= 1) {
          clearInterval(timer)
          return 0
        }
        return prev - 1
      })
    }, 1000)
    
    Alert.alert('提示', '验证码已发送,测试验证码为:123456')
  }

  const handleLogin = async () => {
    if (!phone.trim()) {
      Alert.alert('提示', '请输入手机号')
      return
    }
    if (!code.trim()) {
      Alert.alert('提示', '请输入验证码')
      return
    }

    setLoading(true)
    try {
      const user = await mockLogin(phone, code)
      if (user) {
        login(user)
      } else {
        Alert.alert('登录失败', '验证码错误,请输入:123456')
      }
    } finally {
      setLoading(false)
    }
  }

  return (
    <KeyboardAvoidingView
      style={styles.container}
      behavior={Platform.OS === 'ios' ? 'padding' : 'height'}
    >
      <ScrollView
        contentContainerStyle={styles.scrollContent}
        keyboardShouldPersistTaps="handled"
      >
        {/* 顶部背景 */}
        <View style={styles.header}>
          <Text style={styles.title}>AI健康助手</Text>
          <Text style={styles.subtitle}>您的智能健康管家</Text>
        </View>

        {/* 登录表单 */}
        <View style={styles.form}>
          <Text style={styles.formTitle}>手机号登录</Text>
          
          <TextInput
            label="手机号"
            value={phone}
            onChangeText={setPhone}
            keyboardType="phone-pad"
            maxLength={11}
            style={styles.input}
            mode="outlined"
            outlineColor="#E5E7EB"
            activeOutlineColor="#10B981"
            left={<TextInput.Icon icon="phone" color="#9CA3AF" />}
          />

          <View style={styles.codeRow}>
            <TextInput
              label="验证码"
              value={code}
              onChangeText={setCode}
              keyboardType="number-pad"
              maxLength={6}
              style={[styles.input, styles.codeInput]}
              mode="outlined"
              outlineColor="#E5E7EB"
              activeOutlineColor="#10B981"
              left={<TextInput.Icon icon="shield-check" color="#9CA3AF" />}
            />
            <Button
              mode="outlined"
              onPress={handleSendCode}
              disabled={countdown > 0}
              style={styles.codeButton}
              labelStyle={styles.codeButtonLabel}
            >
              {countdown > 0 ? `${countdown}s` : '获取验证码'}
            </Button>
          </View>

          <Button
            mode="contained"
            onPress={handleLogin}
            loading={loading}
            disabled={loading || !phone.trim() || !code.trim()}
            style={styles.loginButton}
            contentStyle={styles.loginButtonContent}
            buttonColor="#10B981"
          >
            登录
          </Button>

          <Text style={styles.hint}>
            测试账号:13800138000,验证码:123456
          </Text>

          <Text style={styles.agreement}>
            登录即表示同意《用户协议》和《隐私政策》
          </Text>
        </View>
      </ScrollView>
    </KeyboardAvoidingView>
  )
}

const styles = StyleSheet.create({
  container: {
    flex: 1,
    backgroundColor: '#10B981',
  },
  scrollContent: {
    flexGrow: 1,
  },
  header: {
    alignItems: 'center',
    paddingTop: 80,
    paddingBottom: 40,
  },
  title: {
    fontSize: 32,
    fontWeight: 'bold',
    color: '#fff',
    marginBottom: 8,
  },
  subtitle: {
    fontSize: 16,
    color: 'rgba(255,255,255,0.8)',
  },
  form: {
    flex: 1,
    backgroundColor: '#fff',
    borderTopLeftRadius: 24,
    borderTopRightRadius: 24,
    padding: 24,
  },
  formTitle: {
    fontSize: 20,
    fontWeight: '600',
    color: '#1F2937',
    marginBottom: 24,
    textAlign: 'center',
  },
  input: {
    marginBottom: 16,
    backgroundColor: '#fff',
  },
  codeRow: {
    flexDirection: 'row',
    alignItems: 'center',
    gap: 12,
  },
  codeInput: {
    flex: 1,
  },
  codeButton: {
    marginBottom: 16,
    borderColor: '#10B981',
  },
  codeButtonLabel: {
    color: '#10B981',
  },
  loginButton: {
    marginTop: 8,
    borderRadius: 24,
  },
  loginButtonContent: {
    paddingVertical: 8,
  },
  hint: {
    marginTop: 16,
    fontSize: 12,
    color: '#9CA3AF',
    textAlign: 'center',
  },
  agreement: {
    marginTop: 24,
    fontSize: 12,
    color: '#9CA3AF',
    textAlign: 'center',
  },
})

export default LoginScreen

模拟数据说明

登录使用 src/mock/user.ts 中的模拟数据:

  • 测试手机号:13800138000
  • 测试验证码:123456

验收标准

  • 登录页面 UI 正常显示
  • 验证码倒计时正常
  • 正确验证码可登录成功
  • 错误验证码提示错误
  • 登录成功后跳转到首页

预计耗时

20-25 分钟


下一步

完成后进入 02-APP原型开发/04-首页.md