注册账号界面ui调整

This commit is contained in:
2025-11-07 21:25:02 +08:00
parent f86b5e1d39
commit c100a8ceef
2 changed files with 177 additions and 97 deletions

View File

@@ -43,6 +43,10 @@ import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.R import com.aiosman.ravenow.R
import com.aiosman.ravenow.ui.modifiers.noRippleClickable import com.aiosman.ravenow.ui.modifiers.noRippleClickable
private val LabelTextColor = Color(red = 60f / 255f, green = 60f / 255f, blue = 67f / 255f, alpha = 0.6f)
private val HintTextColor = Color(red = 60f / 255f, green = 60f / 255f, blue = 67f / 255f, alpha = 0.3f)
private val PasswordIconColor = Color(red = 17f / 255f, green = 12f / 255f, blue = 19f / 255f)
@Composable @Composable
fun TextInputField( fun TextInputField(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
@@ -52,67 +56,94 @@ fun TextInputField(
label: String? = null, label: String? = null,
hint: String? = null, hint: String? = null,
error: String? = null, error: String? = null,
enabled: Boolean = true enabled: Boolean = true,
leadingIcon: @Composable (() -> Unit)? = null,
customBackgroundColor: Color? = null,
customCornerRadius: Float = 24f
) { ) {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
var showPassword by remember { mutableStateOf(!password) } var showPassword by remember { mutableStateOf(!password) }
var isFocused by remember { mutableStateOf(false) } var isFocused by remember { mutableStateOf(false) }
val backgroundColor = customBackgroundColor ?: AppColors.inputBackground
Column(modifier = modifier) { Column(modifier = modifier) {
label?.let { label?.let {
Text(it, color = AppColors.secondaryText) Text(
Spacer(modifier = Modifier.height(16.dp)) text = it,
color = LabelTextColor,
fontSize = 13.sp,
modifier = Modifier.padding(start = 8.dp, top = 8.dp, bottom = 8.dp)
)
} }
Box( Box(
contentAlignment = Alignment.CenterStart, contentAlignment = Alignment.CenterStart,
modifier = Modifier modifier = Modifier
.clip(RoundedCornerShape(24.dp)) .clip(RoundedCornerShape(customCornerRadius.dp))
.background(AppColors.inputBackground) .background(backgroundColor)
.border( .border(
width = 2.dp, width = 2.dp,
color = if (error == null) Color.Transparent else AppColors.error, color = if (error == null) Color.Transparent else AppColors.error,
shape = RoundedCornerShape(24.dp) shape = RoundedCornerShape(customCornerRadius.dp)
) )
.padding(horizontal = 16.dp, vertical = 16.dp) .padding(horizontal = 16.dp, vertical = 16.dp)
) { ) {
Row(verticalAlignment = Alignment.CenterVertically){ Row(
verticalAlignment = Alignment.CenterVertically,
modifier = Modifier.fillMaxWidth()
){
leadingIcon?.let {
Box(modifier = Modifier.size(24.dp)) {
it()
}
Spacer(modifier = Modifier.size(12.dp))
}
Box(modifier = Modifier.weight(1f)) {
BasicTextField( BasicTextField(
value = text, value = text,
onValueChange = onValueChange, onValueChange = onValueChange,
modifier = Modifier modifier = Modifier
.weight(1f) .fillMaxWidth()
.onFocusChanged { focusState -> .onFocusChanged { focusState ->
isFocused = focusState.isFocused isFocused = focusState.isFocused
}, },
textStyle = TextStyle( textStyle = TextStyle(
fontSize = 16.sp, fontSize = 16.sp,
fontWeight = FontWeight.W500, fontWeight = FontWeight.W400,
color = AppColors.text color = AppColors.text
), ),
keyboardOptions = KeyboardOptions( keyboardOptions = KeyboardOptions(
keyboardType = if (password) KeyboardType.Password else KeyboardType.Text keyboardType = if (password) KeyboardType.Password else KeyboardType.Email
), ),
visualTransformation = if (showPassword) VisualTransformation.None else PasswordVisualTransformation(), visualTransformation = if (showPassword) VisualTransformation.None else PasswordVisualTransformation(),
singleLine = true, singleLine = true,
enabled = enabled, enabled = enabled,
cursorBrush = SolidColor(AppColors.text), cursorBrush = SolidColor(AppColors.text),
) )
if (password) { if (text.isEmpty() && hint != null) {
Image( Text(
painter = painterResource(id = R.drawable.rider_pro_eye), text = hint,
contentDescription = "Password", color = HintTextColor,
modifier = Modifier fontSize = 16.sp,
.size(18.dp) fontWeight = FontWeight.W400
.noRippleClickable {
showPassword = !showPassword
},
colorFilter = ColorFilter.tint(AppColors.text)
) )
} }
} }
if (password) {
if (text.isEmpty()) { Image(
hint?.let { painter = painterResource(
Text(it, modifier = Modifier.padding(start = 5.dp), color = AppColors.inputHint, fontWeight = FontWeight.W600) id = if (showPassword) {
R.drawable.rider_pro_eye
} else {
R.mipmap.icon_eyes_closed_light
}
),
contentDescription = "Password",
modifier = Modifier
.size(24.dp)
.noRippleClickable {
showPassword = !showPassword
},
colorFilter = ColorFilter.tint(PasswordIconColor)
)
} }
} }
} }

View File

@@ -1,15 +1,19 @@
package com.aiosman.ravenow.ui.login package com.aiosman.ravenow.ui.login
import android.widget.Toast import android.widget.Toast
import androidx.compose.foundation.Image
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width import androidx.compose.foundation.layout.width
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@@ -19,9 +23,13 @@ import androidx.compose.runtime.setValue
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import com.aiosman.ravenow.AppState import com.aiosman.ravenow.AppState
import com.aiosman.ravenow.AppStore import com.aiosman.ravenow.AppStore
import com.aiosman.ravenow.LocalAppTheme import com.aiosman.ravenow.LocalAppTheme
@@ -33,25 +41,28 @@ import com.aiosman.ravenow.data.ServiceException
import com.aiosman.ravenow.data.AccountServiceImpl import com.aiosman.ravenow.data.AccountServiceImpl
import com.aiosman.ravenow.data.api.getErrorMessageCode import com.aiosman.ravenow.data.api.getErrorMessageCode
import com.aiosman.ravenow.ui.NavigationRoute import com.aiosman.ravenow.ui.NavigationRoute
import com.aiosman.ravenow.ui.comment.NoticeScreenHeader
import com.aiosman.ravenow.ui.composables.ActionButton import com.aiosman.ravenow.ui.composables.ActionButton
import com.aiosman.ravenow.ui.composables.CheckboxWithLabel import com.aiosman.ravenow.ui.composables.CheckboxWithLabel
import com.aiosman.ravenow.ui.composables.PolicyCheckbox import com.aiosman.ravenow.ui.composables.PolicyCheckbox
import com.aiosman.ravenow.ui.composables.StatusBarSpacer import com.aiosman.ravenow.ui.composables.StatusBarSpacer
import com.aiosman.ravenow.ui.composables.TextInputField import com.aiosman.ravenow.ui.composables.TextInputField
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import com.aiosman.ravenow.utils.PasswordValidator import com.aiosman.ravenow.utils.PasswordValidator
import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
private val LightGrayBackground = Color(red = 250f / 255f, green = 249f / 255f, blue = 251f / 255f)
private val IconGray = Color(red = 151f / 255f, green = 148f / 255f, blue = 153f / 255f)
private val PurpleButton = Color(0xFF7C45ED)
@Composable @Composable
fun EmailSignupScreen() { fun EmailSignupScreen() {
var appColor = LocalAppTheme.current val appColor = LocalAppTheme.current
var email by remember { mutableStateOf("") } var email by remember { mutableStateOf("") }
var password by remember { mutableStateOf("") } var password by remember { mutableStateOf("") }
var confirmPassword by remember { mutableStateOf("") } var confirmPassword by remember { mutableStateOf("") }
var rememberMe by remember { mutableStateOf(false) } var rememberMe by remember { mutableStateOf(false) }
var acceptTerms by remember { mutableStateOf(false) } var acceptTerms by remember { mutableStateOf(false) }
var acceptPromotions by remember { mutableStateOf(false) }
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val navController = LocalNavController.current val navController = LocalNavController.current
val context = LocalContext.current val context = LocalContext.current
@@ -60,7 +71,6 @@ fun EmailSignupScreen() {
var passwordError by remember { mutableStateOf<String?>(null) } var passwordError by remember { mutableStateOf<String?>(null) }
var confirmPasswordError by remember { mutableStateOf<String?>(null) } var confirmPasswordError by remember { mutableStateOf<String?>(null) }
var termsError by remember { mutableStateOf<Boolean>(false) } var termsError by remember { mutableStateOf<Boolean>(false) }
var promotionsError by remember { mutableStateOf<Boolean>(false) }
fun validateForm(): Boolean { fun validateForm(): Boolean {
emailError = when { emailError = when {
// 非空 // 非空
@@ -88,22 +98,8 @@ fun EmailSignupScreen() {
} }
termsError = true termsError = true
return false return false
} else { }
termsError = false termsError = false
}
if (!acceptPromotions) {
scope.launch(Dispatchers.Main) {
Toast.makeText(
context,
context.getString(R.string.error_not_accept_recive_notice),
Toast.LENGTH_SHORT
).show()
}
promotionsError = true
return false
} else {
promotionsError = false
}
return emailError == null && passwordError == null && confirmPasswordError == null return emailError == null && passwordError == null && confirmPasswordError == null
} }
@@ -158,63 +154,127 @@ fun EmailSignupScreen() {
} }
Column( Column(
horizontalAlignment = Alignment.CenterHorizontally,
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(appColor.background) .background(appColor.background)
) { ) {
StatusBarSpacer() StatusBarSpacer()
Box( // 顶部导航栏:返回箭头 + "注册"标题,左对齐
Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(top = 16.dp, start = 16.dp, end = 16.dp) .padding(top = 15.dp, start = 16.dp, bottom = 15.dp, end = 16.dp),
verticalAlignment = Alignment.CenterVertically
) { ) {
NoticeScreenHeader(stringResource(R.string.sign_up_upper), moreIcon = false) Image(
painter = painterResource(id = R.drawable.rider_pro_back_icon),
contentDescription = "Back",
modifier = Modifier
.size(24.dp)
.noRippleClickable {
navController.navigateUp()
},
colorFilter = ColorFilter.tint(Color.Black)
)
Spacer(modifier = Modifier.width(8.dp))
Text(
text = stringResource(R.string.sign_up_upper),
fontSize = 20.sp,
fontWeight = FontWeight.W600,
color = Color.Black
)
} }
Spacer(modifier = Modifier.padding(32.dp))
// 输入区域
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.weight(1f) .weight(1f)
.padding(horizontal = 24.dp) .padding(horizontal = 0.dp)
) { ) {
Spacer(modifier = Modifier.height(16.dp))
// 邮箱输入框
TextInputField( TextInputField(
modifier = Modifier modifier = Modifier
.fillMaxWidth(), .fillMaxWidth()
.padding(horizontal = 24.dp),
text = email, text = email,
onValueChange = { onValueChange = {
email = it email = it
}, },
hint = stringResource(R.string.text_hint_email), label = stringResource(R.string.login_email_label),
error = emailError hint = "输入电子邮件",
error = emailError,
leadingIcon = {
Image(
painter = painterResource(id = R.mipmap.icon_email_light),
contentDescription = "Email",
modifier = Modifier.size(24.dp),
colorFilter = ColorFilter.tint(IconGray)
) )
Spacer(modifier = Modifier.padding(4.dp)) },
customBackgroundColor = LightGrayBackground,
customCornerRadius = 16f
)
// 密码输入框
TextInputField( TextInputField(
modifier = Modifier modifier = Modifier
.fillMaxWidth(), .fillMaxWidth()
.padding(horizontal = 24.dp),
text = password, text = password,
onValueChange = { onValueChange = {
password = it password = it
}, },
password = true, password = true,
label = stringResource(R.string.text_hint_password).replace("输入", ""),
hint = stringResource(R.string.text_hint_password), hint = stringResource(R.string.text_hint_password),
error = passwordError error = passwordError,
leadingIcon = {
Image(
painter = painterResource(id = R.mipmap.icon_lock_light),
contentDescription = "Lock",
modifier = Modifier.size(24.dp),
colorFilter = ColorFilter.tint(IconGray)
) )
Spacer(modifier = Modifier.padding(4.dp)) },
customBackgroundColor = LightGrayBackground,
customCornerRadius = 16f
)
// 确认密码输入框
TextInputField( TextInputField(
modifier = Modifier modifier = Modifier
.fillMaxWidth(), .fillMaxWidth()
.padding(horizontal = 24.dp),
text = confirmPassword, text = confirmPassword,
onValueChange = { onValueChange = {
confirmPassword = it confirmPassword = it
}, },
password = true, password = true,
hint = stringResource(R.string.text_hint_confirm_password), label = stringResource(R.string.confirm_password_label),
error = confirmPasswordError hint = stringResource(R.string.text_hint_password),
error = confirmPasswordError,
leadingIcon = {
Image(
painter = painterResource(id = R.mipmap.icon_lock_light),
contentDescription = "Lock",
modifier = Modifier.size(24.dp),
colorFilter = ColorFilter.tint(IconGray)
) )
Spacer(modifier = Modifier.height(8.dp)) },
customBackgroundColor = LightGrayBackground,
customCornerRadius = 16f
)
Spacer(modifier = Modifier.height(16.dp))
// 功能选项区域
Column( Column(
modifier = Modifier.fillMaxWidth(), modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp),
horizontalAlignment = Alignment.Start, horizontalAlignment = Alignment.Start,
) { ) {
CheckboxWithLabel( CheckboxWithLabel(
@@ -236,42 +296,31 @@ fun EmailSignupScreen() {
termsError = false termsError = false
} }
} }
Spacer(modifier = Modifier.height(16.dp))
CheckboxWithLabel(
checked = acceptPromotions,
checkSize = 16,
fontSize = 12,
label = stringResource(R.string.agree_promotion),
error = promotionsError
) {
acceptPromotions = it
// 当用户勾选时,立即清除错误状态
if (it) {
promotionsError = false
}
}
} }
Spacer(modifier = Modifier.height(32.dp)) Spacer(modifier = Modifier.height(76.dp))
// 底部注册按钮
Box( Box(
modifier = Modifier.fillMaxWidth(), modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 24.dp),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
ActionButton( ActionButton(
modifier = Modifier modifier = Modifier.fillMaxWidth(),
.width(345.dp), text = stringResource(R.string.sign_up_upper),
text = stringResource(R.string.lets_ride_upper), backgroundColor = PurpleButton,
backgroundColor = Color(0xffda3832), color = Color.White,
color = Color.White fontSize = 17.sp,
fontWeight = FontWeight.W600
) { ) {
scope.launch(Dispatchers.IO) { scope.launch(Dispatchers.IO) {
registerUser() registerUser()
} }
} }
} }
}
}
} }
} }