我的-编辑-ui调整
This commit is contained in:
@@ -4,19 +4,26 @@ import android.content.Context
|
|||||||
import android.content.Intent
|
import android.content.Intent
|
||||||
import android.net.Uri
|
import android.net.Uri
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
|
import androidx.compose.foundation.border
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
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.size
|
||||||
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material.icons.Icons
|
import androidx.compose.material.icons.Icons
|
||||||
import androidx.compose.material.icons.filled.Add
|
import androidx.compose.material.icons.filled.Add
|
||||||
|
import androidx.compose.material.icons.filled.ArrowForward
|
||||||
import androidx.compose.material.icons.filled.Check
|
import androidx.compose.material.icons.filled.Check
|
||||||
|
import androidx.compose.material.icons.filled.Edit as EditIcon
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.runtime.LaunchedEffect
|
import androidx.compose.runtime.LaunchedEffect
|
||||||
@@ -28,10 +35,14 @@ import androidx.compose.ui.Alignment
|
|||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
import androidx.compose.ui.layout.ContentScale
|
import androidx.compose.ui.layout.ContentScale
|
||||||
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 androidx.lifecycle.viewModelScope
|
import androidx.lifecycle.viewModelScope
|
||||||
import com.aiosman.ravenow.AppState
|
import com.aiosman.ravenow.AppState
|
||||||
import com.aiosman.ravenow.AppStore
|
import com.aiosman.ravenow.AppStore
|
||||||
@@ -39,26 +50,26 @@ import com.aiosman.ravenow.LocalAppTheme
|
|||||||
import com.aiosman.ravenow.LocalNavController
|
import com.aiosman.ravenow.LocalNavController
|
||||||
import com.aiosman.ravenow.R
|
import com.aiosman.ravenow.R
|
||||||
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.CustomAsyncImage
|
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
|
||||||
import com.aiosman.ravenow.ui.composables.StatusBarMaskLayout
|
import com.aiosman.ravenow.ui.composables.StatusBarMaskLayout
|
||||||
import com.aiosman.ravenow.ui.composables.StatusBarSpacer
|
|
||||||
import com.aiosman.ravenow.ui.composables.form.FormTextInput
|
|
||||||
import com.aiosman.ravenow.ui.composables.debouncedClickable
|
import com.aiosman.ravenow.ui.composables.debouncedClickable
|
||||||
import com.aiosman.ravenow.ui.composables.rememberDebouncedNavigation
|
import com.aiosman.ravenow.ui.composables.rememberDebouncedNavigation
|
||||||
|
import com.google.accompanist.systemuicontroller.rememberSystemUiController
|
||||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||||
import kotlinx.coroutines.Dispatchers
|
import kotlinx.coroutines.Dispatchers
|
||||||
import kotlinx.coroutines.launch
|
import kotlinx.coroutines.launch
|
||||||
import android.util.Log
|
import android.util.Log
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.WindowInsets
|
||||||
|
import androidx.compose.foundation.layout.asPaddingValues
|
||||||
|
import androidx.compose.foundation.layout.offset
|
||||||
|
import androidx.compose.foundation.layout.systemBars
|
||||||
|
import androidx.compose.foundation.text.BasicTextField
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.rememberCoroutineScope
|
import androidx.compose.runtime.rememberCoroutineScope
|
||||||
import androidx.compose.ui.draw.shadow
|
import androidx.compose.ui.draw.shadow
|
||||||
import androidx.compose.ui.graphics.Brush
|
import androidx.compose.ui.graphics.Brush
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.graphics.SolidColor
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
|
||||||
import androidx.compose.ui.unit.sp
|
|
||||||
import com.aiosman.ravenow.ConstVars
|
import com.aiosman.ravenow.ConstVars
|
||||||
import com.aiosman.ravenow.ui.composables.pickupAndCompressLauncher
|
import com.aiosman.ravenow.ui.composables.pickupAndCompressLauncher
|
||||||
import java.io.File
|
import java.io.File
|
||||||
@@ -134,192 +145,28 @@ fun AccountEditScreen2(onUpdateBanner: ((Uri, File, Context) -> Unit)? = null,)
|
|||||||
// 确保显示的是当前登录用户的信息,而不是之前用户的缓存数据
|
// 确保显示的是当前登录用户的信息,而不是之前用户的缓存数据
|
||||||
model.reloadProfile()
|
model.reloadProfile()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 设置状态栏为透明,使用浅色图标(因为顶部背景是深色图片)
|
||||||
|
val systemUiController = rememberSystemUiController()
|
||||||
|
LaunchedEffect(Unit) {
|
||||||
|
systemUiController.setStatusBarColor(Color.Transparent, darkIcons = false)
|
||||||
|
}
|
||||||
|
|
||||||
StatusBarMaskLayout(
|
StatusBarMaskLayout(
|
||||||
modifier = Modifier.background(color = appColors.background).padding(horizontal = 16.dp),
|
modifier = Modifier.background(Color(0xFFFAF9FB)),
|
||||||
darkIcons = !AppState.darkMode,
|
darkIcons = false, // 浅色图标(白色),因为顶部背景是深色
|
||||||
maskBoxBackgroundColor = appColors.background
|
maskBoxBackgroundColor = Color.Transparent
|
||||||
) {
|
) {
|
||||||
Column(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxSize()
|
.fillMaxSize()
|
||||||
.background(color = appColors.background),
|
.background(Color(0xFFFAF9FB))
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
|
||||||
) {
|
) {
|
||||||
//StatusBarSpacer()
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.padding(horizontal = 0.dp, vertical = 16.dp)
|
|
||||||
) {
|
|
||||||
NoticeScreenHeader(
|
|
||||||
title = stringResource(R.string.edit_profile),
|
|
||||||
moreIcon = false
|
|
||||||
) {
|
|
||||||
|
|
||||||
Icon(
|
|
||||||
modifier = Modifier
|
|
||||||
.size(24.dp)
|
|
||||||
.debouncedClickable(
|
|
||||||
enabled = validate() && !model.isUpdating,
|
|
||||||
debounceTime = 1000L
|
|
||||||
) {
|
|
||||||
if (validate() && !model.isUpdating) {
|
|
||||||
model.viewModelScope.launch {
|
|
||||||
model.isUpdating = true
|
|
||||||
model.updateUserProfile(context)
|
|
||||||
model.viewModelScope.launch(Dispatchers.Main) {
|
|
||||||
debouncedNavigation {
|
|
||||||
navController.navigateUp()
|
|
||||||
}
|
|
||||||
model.isUpdating = false
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
},
|
|
||||||
imageVector = Icons.Default.Check,
|
|
||||||
contentDescription = "保存",
|
|
||||||
tint = if (validate() && !model.isUpdating) appColors.text else appColors.nonActiveText
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// 添加横幅图片区域
|
|
||||||
val banner = model.profile?.banner
|
|
||||||
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxWidth()
|
|
||||||
.height(300.dp)
|
|
||||||
.clip(RoundedCornerShape(12.dp))
|
|
||||||
) {
|
|
||||||
if (banner != null) {
|
|
||||||
CustomAsyncImage(
|
|
||||||
context = LocalContext.current,
|
|
||||||
imageUrl = banner,
|
|
||||||
modifier = Modifier.fillMaxSize(),
|
|
||||||
contentDescription = "Banner",
|
|
||||||
contentScale = ContentScale.Crop
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.fillMaxSize()
|
|
||||||
.background(Color.Gray.copy(alpha = 0.1f))
|
|
||||||
)
|
|
||||||
}
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.width(120.dp)
|
|
||||||
.height(42.dp)
|
|
||||||
.align(Alignment.BottomEnd)
|
|
||||||
.padding(end = 12.dp, bottom = 12.dp)
|
|
||||||
.background(
|
|
||||||
color = Color.Black.copy(alpha = 0.4f),
|
|
||||||
shape = RoundedCornerShape(9.dp)
|
|
||||||
)
|
|
||||||
.noRippleClickable {
|
|
||||||
Intent(Intent.ACTION_PICK).apply {
|
|
||||||
type = "image/*"
|
|
||||||
pickBannerImageLauncher.launch(this)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
){
|
|
||||||
Text(
|
|
||||||
text = "change",
|
|
||||||
fontSize = 14.sp,
|
|
||||||
fontWeight = FontWeight.W600,
|
|
||||||
color = Color.White,
|
|
||||||
modifier = Modifier.align(Alignment.Center)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(20.dp))
|
|
||||||
|
|
||||||
// 显示内容或加载状态
|
|
||||||
Log.d("AccountEditScreen2", "UI状态 - profile: ${model.profile?.nickName}, isLoading: ${model.isLoading}")
|
|
||||||
when {
|
when {
|
||||||
model.profile != null -> {
|
|
||||||
Log.d("AccountEditScreen2", "显示用户资料内容")
|
|
||||||
// 有数据时显示内容
|
|
||||||
val it = model.profile!!
|
|
||||||
Box(
|
|
||||||
modifier = Modifier.size(88.dp),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
CustomAsyncImage(
|
|
||||||
context,
|
|
||||||
model.croppedBitmap ?: it.avatar,
|
|
||||||
modifier = Modifier
|
|
||||||
.size(88.dp)
|
|
||||||
.clip(
|
|
||||||
RoundedCornerShape(88.dp)
|
|
||||||
),
|
|
||||||
contentDescription = "",
|
|
||||||
contentScale = ContentScale.Crop
|
|
||||||
)
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.size(32.dp)
|
|
||||||
.clip(CircleShape)
|
|
||||||
.background(
|
|
||||||
brush = Brush.linearGradient(
|
|
||||||
colors = listOf(
|
|
||||||
Color(0xFF7c45ed),
|
|
||||||
Color(0x997c68ef),
|
|
||||||
Color(0xFF7bd8f8)
|
|
||||||
)
|
|
||||||
),
|
|
||||||
)
|
|
||||||
.align(Alignment.BottomEnd)
|
|
||||||
.debouncedClickable(
|
|
||||||
debounceTime = 800L
|
|
||||||
) {
|
|
||||||
debouncedNavigation {
|
|
||||||
navController.navigate(NavigationRoute.ImageCrop.route)
|
|
||||||
}
|
|
||||||
},
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
Icon(
|
|
||||||
Icons.Default.Add,
|
|
||||||
contentDescription = "Add",
|
|
||||||
tint = Color.White,
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Spacer(modifier = Modifier.height(18.dp))
|
|
||||||
Column(
|
|
||||||
modifier = Modifier
|
|
||||||
.weight(1f)
|
|
||||||
.padding(horizontal = 16.dp)
|
|
||||||
) {
|
|
||||||
FormTextInput(
|
|
||||||
value = model.name,
|
|
||||||
label = stringResource(R.string.nickname),
|
|
||||||
hint = "Input nickname",
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
error = usernameError
|
|
||||||
) { value ->
|
|
||||||
onNicknameChange(value)
|
|
||||||
}
|
|
||||||
FormTextInput(
|
|
||||||
value = model.bio,
|
|
||||||
label = stringResource(R.string.bio),
|
|
||||||
hint = "Input bio",
|
|
||||||
modifier = Modifier.fillMaxWidth(),
|
|
||||||
error = bioError
|
|
||||||
) { value ->
|
|
||||||
onBioChange(value)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
model.isLoading -> {
|
model.isLoading -> {
|
||||||
Log.d("AccountEditScreen2", "显示加载指示器")
|
|
||||||
// 加载中状态
|
// 加载中状态
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxSize(),
|
||||||
.fillMaxSize()
|
|
||||||
.padding(16.dp),
|
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
androidx.compose.material3.CircularProgressIndicator(
|
androidx.compose.material3.CircularProgressIndicator(
|
||||||
@@ -327,24 +174,460 @@ fun AccountEditScreen2(onUpdateBanner: ((Uri, File, Context) -> Unit)? = null,)
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
model.profile != null -> {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxSize()
|
||||||
|
) {
|
||||||
|
// 顶部背景区域(圆角在底部,覆盖状态栏)
|
||||||
|
val banner = model.profile?.banner
|
||||||
|
val statusBarPadding = WindowInsets.systemBars.asPaddingValues().calculateTopPadding()
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.offset(y = -statusBarPadding)
|
||||||
|
) {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.width(402.dp)
|
||||||
|
.height(206.dp)
|
||||||
|
.align(Alignment.TopCenter)
|
||||||
|
.clip(RoundedCornerShape(bottomStart = 32.dp, bottomEnd = 32.dp))
|
||||||
|
) {
|
||||||
|
if (banner != null) {
|
||||||
|
CustomAsyncImage(
|
||||||
|
context = context,
|
||||||
|
imageUrl = banner,
|
||||||
|
modifier = Modifier.fillMaxSize(),
|
||||||
|
contentDescription = "Banner",
|
||||||
|
contentScale = ContentScale.Crop
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.background(Color.Gray.copy(alpha = 0.2f))
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 更换封面按钮(位于壁纸右下方)
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.align(Alignment.BottomEnd)
|
||||||
|
.padding(end = 16.dp, bottom = 20.dp)
|
||||||
|
.clip(RoundedCornerShape(12.dp))
|
||||||
|
.background(Color(0x5C7D7C80)) // RGB(125, 120, 128, 0.36)
|
||||||
|
.padding(horizontal = 8.dp, vertical = 4.dp)
|
||||||
|
.noRippleClickable {
|
||||||
|
Intent(Intent.ACTION_PICK).apply {
|
||||||
|
type = "image/*"
|
||||||
|
pickBannerImageLauncher.launch(this)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||||
|
) {
|
||||||
|
// 更换封面图标
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(
|
||||||
|
id = if (AppState.darkMode) {
|
||||||
|
// TODO: 添加更换封面暗色模式图标
|
||||||
|
R.mipmap.frame_4 // 临时占位,需替换为实际图标
|
||||||
|
} else {
|
||||||
|
// TODO: 添加更换封面亮色模式图标
|
||||||
|
R.mipmap.fengm // 临时占位,需替换为实际图标
|
||||||
|
}
|
||||||
|
),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(16.dp),
|
||||||
|
tint = Color.White
|
||||||
|
)
|
||||||
|
Text(
|
||||||
|
text = "更换封面",
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = Color.White
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 状态栏区域(时间、信号、电池)
|
||||||
|
// 这里使用系统状态栏,不单独实现
|
||||||
|
|
||||||
|
// 导航栏区域
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
) {
|
||||||
|
Spacer(modifier = Modifier.height(statusBarPadding + 12.dp))
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(bottom = 12.dp)
|
||||||
|
) {
|
||||||
|
// 返回按钮
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(44.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(Color.White.copy(alpha = 0.3f))
|
||||||
|
.noRippleClickable {
|
||||||
|
navController.navigateUp()
|
||||||
|
}
|
||||||
|
.align(Alignment.CenterStart),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Image(
|
||||||
|
painter = painterResource(id = R.drawable.rider_pro_back_icon),
|
||||||
|
contentDescription = "Back",
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
colorFilter = ColorFilter.tint(Color.White)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
// 标题
|
||||||
|
Text(
|
||||||
|
text = "编辑资料",
|
||||||
|
fontSize = 17.sp,
|
||||||
|
fontWeight = FontWeight.SemiBold,
|
||||||
|
color = Color.White,
|
||||||
|
modifier = Modifier.align(Alignment.Center)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// 用户头像区域(距离顶部-50dp,包含状态栏高度,左右居中)
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.offset(y = (-50).dp - statusBarPadding),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
val it = model.profile!!
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.size(96.dp),
|
||||||
|
contentAlignment = Alignment.BottomEnd
|
||||||
|
) {
|
||||||
|
// 头像
|
||||||
|
CustomAsyncImage(
|
||||||
|
context,
|
||||||
|
model.croppedBitmap ?: it.avatar,
|
||||||
|
modifier = Modifier
|
||||||
|
.size(96.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.border(2.4.dp, Color(0xFFFAF9FB), CircleShape),
|
||||||
|
contentDescription = "",
|
||||||
|
contentScale = ContentScale.Crop
|
||||||
|
)
|
||||||
|
|
||||||
|
// 编辑头像按钮
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.size(28.dp)
|
||||||
|
.clip(CircleShape)
|
||||||
|
.background(Color(0xFF110C13))
|
||||||
|
.border(2.dp, Color.White, CircleShape)
|
||||||
|
.debouncedClickable(debounceTime = 800L) {
|
||||||
|
debouncedNavigation {
|
||||||
|
navController.navigate(NavigationRoute.ImageCrop.route)
|
||||||
|
}
|
||||||
|
},
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(
|
||||||
|
id = if (AppState.darkMode) {
|
||||||
|
// TODO: 添加编辑头像暗色模式图标
|
||||||
|
R.mipmap.frame_4 // 临时占位,需替换为实际图标
|
||||||
|
} else {
|
||||||
|
// TODO: 添加编辑头像亮色模式图标
|
||||||
|
R.mipmap.bi // 临时占位,需替换为实际图标
|
||||||
|
}
|
||||||
|
),
|
||||||
|
contentDescription = "Edit Avatar",
|
||||||
|
modifier = Modifier.size(16.dp),
|
||||||
|
tint = Color.White
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.weight(1f)
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.offset(y = (-74).dp)//
|
||||||
|
) {
|
||||||
|
// 昵称输入框
|
||||||
|
ProfileInfoCard(
|
||||||
|
label = "昵称",
|
||||||
|
value = model.name,
|
||||||
|
placeholder = "Value",
|
||||||
|
onValueChange = { onNicknameChange(it) },
|
||||||
|
isMultiline = false
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
// 个人简介输入框
|
||||||
|
ProfileInfoCard(
|
||||||
|
label = "个人简介",
|
||||||
|
value = model.bio,
|
||||||
|
placeholder = "Welcome to my fantiac word i will show you something about magic",
|
||||||
|
onValueChange = { onBioChange(it) },
|
||||||
|
isMultiline = true
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
|
||||||
|
// MBTI 类型和星座
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.clip(RoundedCornerShape(16.dp))
|
||||||
|
.background(Color.White)
|
||||||
|
) {
|
||||||
|
// MBTI 类型
|
||||||
|
ProfileSelectItem(
|
||||||
|
label = "MBTI 类型",
|
||||||
|
value = model.mbti ?: "ENFP",
|
||||||
|
iconColor = Color(0xFF7C45ED),
|
||||||
|
iconResDark = null, // TODO: 添加MBTI暗色模式图标
|
||||||
|
iconResLight = null, // TODO: 添加MBTI亮色模式图标
|
||||||
|
onClick = {
|
||||||
|
debouncedNavigation {
|
||||||
|
navController.navigate(NavigationRoute.MbtiSelect.route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
|
||||||
|
// 分隔线
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(0.3.dp)
|
||||||
|
.background(Color(0x41413C43).copy(alpha = 0.2f))
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
// 星座(使用当前图标)
|
||||||
|
ProfileSelectItem(
|
||||||
|
label = "星座",
|
||||||
|
value = model.zodiac ?: "白羊座",
|
||||||
|
iconColor = Color(0xFFFFCC00),
|
||||||
|
iconResDark = R.mipmap.frame_4, // 星座暗色模式图标
|
||||||
|
iconResLight = R.mipmap.xingzuo, // 星座亮色模式图标
|
||||||
|
onClick = {
|
||||||
|
debouncedNavigation {
|
||||||
|
navController.navigate(NavigationRoute.ZodiacSelect.route)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.weight(1f))
|
||||||
|
|
||||||
|
// 保存按钮
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(50.dp)
|
||||||
|
.clip(RoundedCornerShape(1000.dp))
|
||||||
|
.background(
|
||||||
|
brush = Brush.horizontalGradient(
|
||||||
|
colors = listOf(
|
||||||
|
Color(0xFF7C45ED), // RGB(124, 69, 237) - 左侧
|
||||||
|
Color(0xFF7C57EE), // RGB(124, 87, 238) - 中间
|
||||||
|
Color(0xFF7BD8F8) // RGB(123, 216, 248) - 右侧
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
.debouncedClickable(
|
||||||
|
enabled = validate() && !model.isUpdating,
|
||||||
|
debounceTime = 1000L
|
||||||
|
) {
|
||||||
|
if (validate() && !model.isUpdating) {
|
||||||
|
model.viewModelScope.launch {
|
||||||
|
model.isUpdating = true
|
||||||
|
model.updateUserProfile(context)
|
||||||
|
model.viewModelScope.launch(Dispatchers.Main) {
|
||||||
|
debouncedNavigation {
|
||||||
|
navController.navigateUp()
|
||||||
|
}
|
||||||
|
model.isUpdating = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = "保存",
|
||||||
|
fontSize = 17.sp,
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
color = Color.White
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
else -> {
|
else -> {
|
||||||
Log.d("AccountEditScreen2", "显示错误信息 - 没有数据且不在加载中")
|
|
||||||
// 没有数据且不在加载中,显示错误信息
|
// 没有数据且不在加载中,显示错误信息
|
||||||
Box(
|
Box(
|
||||||
modifier = Modifier
|
modifier = Modifier.fillMaxSize(),
|
||||||
.fillMaxSize()
|
|
||||||
.padding(16.dp),
|
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
androidx.compose.material3.Text(
|
Text(
|
||||||
text = "加载用户资料失败,请重试",
|
text = "加载用户资料失败,请重试",
|
||||||
color = appColors.text
|
color = appColors.text
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
}}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 信息输入卡片组件
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun ProfileInfoCard(
|
||||||
|
label: String,
|
||||||
|
value: String,
|
||||||
|
placeholder: String,
|
||||||
|
onValueChange: (String) -> Unit,
|
||||||
|
isMultiline: Boolean = false
|
||||||
|
) {
|
||||||
|
val appColors = LocalAppTheme.current
|
||||||
|
|
||||||
|
Box(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(if (isMultiline) 66.dp else 56.dp) // 昵称框高度56dp,个人简介66dp
|
||||||
|
.clip(RoundedCornerShape(16.dp))
|
||||||
|
.background(Color.White),
|
||||||
|
contentAlignment = if (isMultiline) Alignment.TopStart else Alignment.CenterStart
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(horizontal = 16.dp)
|
||||||
|
.padding(vertical = if (isMultiline) 11.dp else 0.dp),
|
||||||
|
verticalAlignment = if (isMultiline) Alignment.Top else Alignment.CenterVertically
|
||||||
|
) {
|
||||||
|
// 标签
|
||||||
|
Text(
|
||||||
|
text = label,
|
||||||
|
fontSize = 17.sp,
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
color = Color.Black,
|
||||||
|
modifier = Modifier.width(100.dp)
|
||||||
|
)
|
||||||
|
|
||||||
|
Spacer(modifier = Modifier.width(16.dp))
|
||||||
|
|
||||||
|
// 输入框
|
||||||
|
Box(
|
||||||
|
modifier = Modifier.weight(1f)
|
||||||
|
) {
|
||||||
|
if (value.isEmpty()) {
|
||||||
|
Text(
|
||||||
|
text = placeholder,
|
||||||
|
fontSize = if (isMultiline) 15.sp else 17.sp,
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
color = Color(0x993C3C43),
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
BasicTextField(
|
||||||
|
value = value,
|
||||||
|
onValueChange = onValueChange,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
textStyle = androidx.compose.ui.text.TextStyle(
|
||||||
|
fontSize = if (isMultiline) 15.sp else 17.sp,
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
color = Color.Black
|
||||||
|
),
|
||||||
|
cursorBrush = SolidColor(Color.Black),
|
||||||
|
maxLines = if (isMultiline) Int.MAX_VALUE else 1,
|
||||||
|
singleLine = !isMultiline
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 选择项组件(MBTI、星座)
|
||||||
|
*/
|
||||||
|
@Composable
|
||||||
|
fun ProfileSelectItem(
|
||||||
|
label: String,
|
||||||
|
value: String,
|
||||||
|
iconColor: Color,
|
||||||
|
onClick: () -> Unit,
|
||||||
|
iconResDark: Int? = null,
|
||||||
|
iconResLight: Int? = null
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.height(56.dp)
|
||||||
|
.clickable(onClick = onClick)
|
||||||
|
.padding(horizontal = 16.dp),
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.SpaceBetween
|
||||||
|
) {
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||||
|
) {
|
||||||
|
// 自定义图标
|
||||||
|
Icon(
|
||||||
|
painter = painterResource(
|
||||||
|
id = if (AppState.darkMode) {
|
||||||
|
iconResDark ?: R.mipmap.frame_4 // 使用传入的暗色模式图标,或默认占位
|
||||||
|
} else {
|
||||||
|
iconResLight ?: R.mipmap.naoz // 使用传入的亮色模式图标,或默认占位
|
||||||
|
}
|
||||||
|
),
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(24.dp),
|
||||||
|
tint = iconColor
|
||||||
|
)
|
||||||
|
|
||||||
|
Text(
|
||||||
|
text = label,
|
||||||
|
fontSize = 17.sp,
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
color = Color.Black
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
Row(
|
||||||
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
|
horizontalArrangement = Arrangement.spacedBy(16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = value,
|
||||||
|
fontSize = 17.sp,
|
||||||
|
fontWeight = FontWeight.Normal,
|
||||||
|
color = Color(0x993C3C43)
|
||||||
|
)
|
||||||
|
|
||||||
|
Icon(
|
||||||
|
imageVector = Icons.Default.ArrowForward,
|
||||||
|
contentDescription = null,
|
||||||
|
modifier = Modifier.size(8.dp),
|
||||||
|
tint = Color(0x4D3C3C43)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
Reference in New Issue
Block a user