refactor: 优化前端登录注册界面

- 优化LoginScreen登录界面
- 优化RegisterScreen注册界面
- 优化AuthViewModel认证ViewModel
This commit is contained in:
iammm0
2026-01-27 14:30:45 +08:00
parent b13877dcb7
commit ffa5b425f3
3 changed files with 175 additions and 29 deletions

View File

@@ -32,6 +32,8 @@ fun LoginScreen(
onLoginSuccess: () -> Unit, onLoginSuccess: () -> Unit,
onNavigateToRegister: () -> Unit, onNavigateToRegister: () -> Unit,
onNavigateToResetPassword: (() -> Unit)? = null, onNavigateToResetPassword: (() -> Unit)? = null,
onNavigateToTerms: () -> Unit = {},
onNavigateToPrivacy: () -> Unit = {},
viewModel: AuthViewModel = viewModel( viewModel: AuthViewModel = viewModel(
factory = ViewModelFactory(LocalContext.current) factory = ViewModelFactory(LocalContext.current)
) )
@@ -43,6 +45,7 @@ fun LoginScreen(
var password by remember { mutableStateOf("") } var password by remember { mutableStateOf("") }
var passwordVisible by remember { mutableStateOf(false) } var passwordVisible by remember { mutableStateOf(false) }
var smsCode by remember { mutableStateOf("") } var smsCode by remember { mutableStateOf("") }
var agreedToTerms by remember { mutableStateOf(false) }
var showResultDialog by remember { mutableStateOf(false) } var showResultDialog by remember { mutableStateOf(false) }
val isLoading by viewModel.isLoading.collectAsState() val isLoading by viewModel.isLoading.collectAsState()
@@ -211,16 +214,20 @@ fun LoginScreen(
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
// 验证码输入框 // 验证码输入框 - 居中显示
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
SmsCodeInput( SmsCodeInput(
code = smsCode, code = smsCode,
onCodeChange = { smsCode = it }, onCodeChange = { smsCode = it },
codeLength = 6, codeLength = 6,
enabled = !isLoading, enabled = !isLoading
modifier = Modifier.fillMaxWidth()
) )
} }
} }
}
Spacer(modifier = Modifier.height(32.dp)) Spacer(modifier = Modifier.height(32.dp))
@@ -250,19 +257,84 @@ fun LoginScreen(
) )
} }
Spacer(modifier = Modifier.height(24.dp))
// 同意协议复选框 - 使用Column支持换行
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.Top,
horizontalArrangement = Arrangement.Start
) {
Checkbox(
checked = agreedToTerms,
onCheckedChange = { agreedToTerms = it },
enabled = !isLoading,
modifier = Modifier.padding(top = 2.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Column(
modifier = Modifier.weight(1f)
) {
Row(
modifier = Modifier.wrapContentWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "我已阅读并同意",
fontSize = 12.sp,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
TextButton(
onClick = onNavigateToTerms,
contentPadding = PaddingValues(horizontal = 4.dp, vertical = 0.dp),
modifier = Modifier.height(24.dp)
) {
Text(
text = "《用户协议》",
fontSize = 12.sp,
color = LightPurple
)
}
}
Row(
modifier = Modifier.wrapContentWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "",
fontSize = 12.sp,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
TextButton(
onClick = onNavigateToPrivacy,
contentPadding = PaddingValues(horizontal = 4.dp, vertical = 0.dp),
modifier = Modifier.height(24.dp)
) {
Text(
text = "《隐私政策》",
fontSize = 12.sp,
color = LightPurple
)
}
}
}
}
Spacer(modifier = Modifier.height(16.dp))
// 登录按钮 // 登录按钮
Button( Button(
onClick = { onClick = {
val trimmedPhone = phone.trim() val trimmedPhone = phone.trim()
if (isPasswordMode) { if (isPasswordMode) {
// 密码登录 // 密码登录
if (trimmedPhone.length == 11 && password.length >= 6) { if (trimmedPhone.length == 11 && password.length >= 6 && agreedToTerms) {
viewModel.login(trimmedPhone, password) viewModel.login(trimmedPhone, password, agreedToTerms)
} }
} else { } else {
// 验证码登录 // 验证码登录
if (trimmedPhone.length == 11 && smsCode.length == 6) { if (trimmedPhone.length == 11 && smsCode.length == 6 && agreedToTerms) {
viewModel.loginWithSms(trimmedPhone, smsCode) viewModel.loginWithSms(trimmedPhone, smsCode, agreedToTerms)
} }
} }
}, },
@@ -270,7 +342,8 @@ fun LoginScreen(
.fillMaxWidth() .fillMaxWidth()
.height(48.dp), .height(48.dp),
enabled = !isLoading && phone.trim().length == 11 && enabled = !isLoading && phone.trim().length == 11 &&
(if (isPasswordMode) password.length >= 6 else smsCode.length == 6), (if (isPasswordMode) password.length >= 6 else smsCode.length == 6) &&
agreedToTerms,
colors = ButtonDefaults.buttonColors( colors = ButtonDefaults.buttonColors(
containerColor = LightPurple, containerColor = LightPurple,
disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant disabledContainerColor = MaterialTheme.colorScheme.surfaceVariant

View File

@@ -32,6 +32,8 @@ import com.huaga.life_echo.ui.viewmodel.ViewModelFactory
@Composable @Composable
fun RegisterScreen( fun RegisterScreen(
onRegisterSuccess: () -> Unit, onRegisterSuccess: () -> Unit,
onNavigateToTerms: () -> Unit = {},
onNavigateToPrivacy: () -> Unit = {},
viewModel: AuthViewModel = viewModel( viewModel: AuthViewModel = viewModel(
factory = ViewModelFactory(LocalContext.current) factory = ViewModelFactory(LocalContext.current)
) )
@@ -44,6 +46,7 @@ fun RegisterScreen(
var email by remember { mutableStateOf("") } var email by remember { mutableStateOf("") }
var passwordVisible by remember { mutableStateOf(false) } var passwordVisible by remember { mutableStateOf(false) }
var confirmPasswordVisible by remember { mutableStateOf(false) } var confirmPasswordVisible by remember { mutableStateOf(false) }
var agreedToTerms by remember { mutableStateOf(false) }
var showResultDialog by remember { mutableStateOf(false) } var showResultDialog by remember { mutableStateOf(false) }
val isLoading by viewModel.isLoading.collectAsState() val isLoading by viewModel.isLoading.collectAsState()
@@ -75,7 +78,8 @@ fun RegisterScreen(
smsCode.length == 6 && smsCode.length == 6 &&
password.length >= 6 && password.length >= 6 &&
password == confirmPassword && password == confirmPassword &&
nickname.isNotBlank() nickname.isNotBlank() &&
agreedToTerms
// 错误消息Snackbar // 错误消息Snackbar
val snackbarHostState = remember { SnackbarHostState() } val snackbarHostState = remember { SnackbarHostState() }
@@ -168,14 +172,19 @@ fun RegisterScreen(
color = MaterialTheme.colorScheme.onSurfaceVariant color = MaterialTheme.colorScheme.onSurfaceVariant
) )
// 验证码输入框 - 居中显示
Box(
modifier = Modifier.fillMaxWidth(),
contentAlignment = Alignment.Center
) {
SmsCodeInput( SmsCodeInput(
code = smsCode, code = smsCode,
onCodeChange = { smsCode = it }, onCodeChange = { smsCode = it },
enabled = !isLoading, enabled = !isLoading
modifier = Modifier.fillMaxWidth()
) )
} }
} }
}
item { item {
// 密码输入框 // 密码输入框
@@ -273,6 +282,69 @@ fun RegisterScreen(
item { item {
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
// 同意协议复选框 - 使用Column支持换行
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.Top,
horizontalArrangement = Arrangement.Start
) {
Checkbox(
checked = agreedToTerms,
onCheckedChange = { agreedToTerms = it },
enabled = !isLoading,
modifier = Modifier.padding(top = 2.dp)
)
Spacer(modifier = Modifier.width(8.dp))
Column(
modifier = Modifier.weight(1f)
) {
Row(
modifier = Modifier.wrapContentWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "我已阅读并同意",
fontSize = 12.sp,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
TextButton(
onClick = onNavigateToTerms,
contentPadding = PaddingValues(horizontal = 4.dp, vertical = 0.dp),
modifier = Modifier.height(24.dp)
) {
Text(
text = "《用户协议》",
fontSize = 12.sp,
color = LightPurple
)
}
}
Row(
modifier = Modifier.wrapContentWidth(),
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = "",
fontSize = 12.sp,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
TextButton(
onClick = onNavigateToPrivacy,
contentPadding = PaddingValues(horizontal = 4.dp, vertical = 0.dp),
modifier = Modifier.height(24.dp)
) {
Text(
text = "《隐私政策》",
fontSize = 12.sp,
color = LightPurple
)
}
}
}
}
Spacer(modifier = Modifier.height(16.dp))
// 注册按钮 // 注册按钮
Button( Button(
onClick = { onClick = {
@@ -282,7 +354,8 @@ fun RegisterScreen(
code = smsCode, code = smsCode,
password = password, password = password,
nickname = nickname, nickname = nickname,
email = if (email.isNotBlank()) email else null email = if (email.isNotBlank()) email else null,
agreedToTerms = agreedToTerms
) )
} }
}, },

View File

@@ -68,14 +68,14 @@ class AuthViewModel(private val context: Context) : ViewModel() {
/** /**
* 用户登录 * 用户登录
*/ */
fun login(phone: String, password: String) { fun login(phone: String, password: String, agreedToTerms: Boolean) {
viewModelScope.launch { viewModelScope.launch {
_isLoading.value = true _isLoading.value = true
_errorMessage.value = null _errorMessage.value = null
_successMessage.value = null _successMessage.value = null
_operationResult.value = null _operationResult.value = null
val result = authService.login(phone, password) val result = authService.login(phone, password, agreedToTerms)
result.fold( result.fold(
onSuccess = { tokenResponse -> onSuccess = { tokenResponse ->
@@ -127,14 +127,14 @@ class AuthViewModel(private val context: Context) : ViewModel() {
/** /**
* 用户注册 * 用户注册
*/ */
fun register(phone: String, password: String, nickname: String, email: String? = null) { fun register(phone: String, password: String, nickname: String, email: String? = null, agreedToTerms: Boolean) {
viewModelScope.launch { viewModelScope.launch {
_isLoading.value = true _isLoading.value = true
_errorMessage.value = null _errorMessage.value = null
_successMessage.value = null _successMessage.value = null
_operationResult.value = null _operationResult.value = null
val result = authService.register(phone, password, nickname, email) val result = authService.register(phone, password, nickname, email, agreedToTerms)
result.fold( result.fold(
onSuccess = { tokenResponse -> onSuccess = { tokenResponse ->
@@ -340,14 +340,14 @@ class AuthViewModel(private val context: Context) : ViewModel() {
/** /**
* 验证码登录 * 验证码登录
*/ */
fun loginWithSms(phone: String, code: String) { fun loginWithSms(phone: String, code: String, agreedToTerms: Boolean) {
viewModelScope.launch { viewModelScope.launch {
_isLoading.value = true _isLoading.value = true
_errorMessage.value = null _errorMessage.value = null
_successMessage.value = null _successMessage.value = null
_operationResult.value = null _operationResult.value = null
val result = authService.loginWithSms(phone, code) val result = authService.loginWithSms(phone, code, agreedToTerms)
result.fold( result.fold(
onSuccess = { tokenResponse -> onSuccess = { tokenResponse ->
@@ -391,14 +391,14 @@ class AuthViewModel(private val context: Context) : ViewModel() {
/** /**
* 验证码注册 * 验证码注册
*/ */
fun registerWithSms(phone: String, code: String, password: String, nickname: String, email: String? = null) { fun registerWithSms(phone: String, code: String, password: String, nickname: String, email: String? = null, agreedToTerms: Boolean) {
viewModelScope.launch { viewModelScope.launch {
_isLoading.value = true _isLoading.value = true
_errorMessage.value = null _errorMessage.value = null
_successMessage.value = null _successMessage.value = null
_operationResult.value = null _operationResult.value = null
val result = authService.registerWithSms(phone, code, password, nickname, email) val result = authService.registerWithSms(phone, code, password, nickname, email, agreedToTerms)
result.fold( result.fold(
onSuccess = { tokenResponse -> onSuccess = { tokenResponse ->