refactor: 优化前端登录注册界面
- 优化LoginScreen登录界面 - 优化RegisterScreen注册界面 - 优化AuthViewModel认证ViewModel
This commit is contained in:
@@ -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
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
|||||||
@@ -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 ->
|
||||||
|
|||||||
Reference in New Issue
Block a user