From 6a4b140da6bf45352f6aa6a03d03a9c89acd8faf Mon Sep 17 00:00:00 2001 From: iammm0 Date: Thu, 29 Jan 2026 10:57:22 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E6=96=B0=E5=A2=9E=E4=B8=AA=E4=BA=BA?= =?UTF-8?q?=E4=BF=A1=E6=81=AF=E7=AE=A1=E7=90=86=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 新增PersonalInfoCard个人信息卡片组件 - 新增PersonalInfoScreen个人信息屏幕 - 更新AppNavigation导航结构 --- .../life_echo/navigation/AppNavigation.kt | 33 +-- .../ui/components/profile/PersonalInfoCard.kt | 97 +++++++++ .../ui/screens/PersonalInfoScreen.kt | 195 ++++++++++++++++++ 3 files changed, 302 insertions(+), 23 deletions(-) create mode 100644 app-android/app/src/main/java/com/huaga/life_echo/ui/components/profile/PersonalInfoCard.kt create mode 100644 app-android/app/src/main/java/com/huaga/life_echo/ui/screens/PersonalInfoScreen.kt diff --git a/app-android/app/src/main/java/com/huaga/life_echo/navigation/AppNavigation.kt b/app-android/app/src/main/java/com/huaga/life_echo/navigation/AppNavigation.kt index 74edf71..7c4fba3 100644 --- a/app-android/app/src/main/java/com/huaga/life_echo/navigation/AppNavigation.kt +++ b/app-android/app/src/main/java/com/huaga/life_echo/navigation/AppNavigation.kt @@ -16,8 +16,8 @@ sealed class Screen(val route: String) { } object MyMemoir : Screen("my_memoir") object Profile : Screen("profile") + object PersonalInfo : Screen("personal_info") object Login : Screen("login") - object Register : Screen("register") object ResetPassword : Screen("reset_password") object AccountManagement : Screen("account_management") object UpgradePlan : Screen("upgrade_plan") @@ -88,6 +88,15 @@ fun AppNavigation( ) { ProfileScreen(navController = navController) } + composable( + route = Screen.PersonalInfo.route, + enterTransition = { slideInHorizontally() }, + exitTransition = { slideOutHorizontally() }, + popEnterTransition = { slideInHorizontallyFromLeft() }, + popExitTransition = { slideOutHorizontallyToRight() } + ) { + PersonalInfoScreen(navController = navController) + } composable( route = Screen.UpgradePlan.route, enterTransition = { slideInVertically() }, @@ -190,9 +199,6 @@ fun AppNavigation( popUpTo(Screen.Login.route) { inclusive = true } } }, - onNavigateToRegister = { - navController.navigate(Screen.Register.route) - }, onNavigateToResetPassword = { navController.navigate(Screen.ResetPassword.route) }, @@ -204,25 +210,6 @@ fun AppNavigation( } ) } - composable( - route = Screen.Register.route, - enterTransition = { slideInHorizontally() }, - exitTransition = { slideOutHorizontally() }, - popEnterTransition = { slideInHorizontallyFromLeft() }, - popExitTransition = { slideOutHorizontallyToRight() } - ) { - RegisterScreen( - onRegisterSuccess = { - navController.popBackStack() - }, - onNavigateToTerms = { - navController.navigate(Screen.Terms.route) - }, - onNavigateToPrivacy = { - navController.navigate(Screen.Privacy.route) - } - ) - } composable( route = Screen.ResetPassword.route, enterTransition = { slideInHorizontally() }, diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/components/profile/PersonalInfoCard.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/components/profile/PersonalInfoCard.kt new file mode 100644 index 0000000..5040e7c --- /dev/null +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/components/profile/PersonalInfoCard.kt @@ -0,0 +1,97 @@ +package com.huaga.life_echo.ui.components.profile + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clip +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import com.huaga.life_echo.ui.icons.AppIcons + +@Composable +fun PersonalInfoCard( + nickname: String, + phone: String, + planName: String, + onClick: () -> Unit, + modifier: Modifier = Modifier +) { + Card( + modifier = modifier + .fillMaxWidth() + .clickable(onClick = onClick), + shape = RoundedCornerShape(12.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surface + ) + ) { + Row( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + // 左侧:图标和信息 + Row( + modifier = Modifier.weight(1f), + horizontalArrangement = Arrangement.spacedBy(12.dp), + verticalAlignment = Alignment.CenterVertically + ) { + // 图标 + Box( + modifier = Modifier + .size(40.dp) + .clip(RoundedCornerShape(8.dp)) + .background(MaterialTheme.colorScheme.primaryContainer), + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = AppIcons.Person, + contentDescription = "个人信息", + tint = MaterialTheme.colorScheme.primary, + modifier = Modifier.size(24.dp) + ) + } + + // 信息 + Column( + modifier = Modifier.weight(1f), + verticalArrangement = Arrangement.spacedBy(4.dp) + ) { + Text( + text = nickname, + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onSurface + ) + Text( + text = phone, + fontSize = 12.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + + // 右侧:套餐标签和箭头 + Row( + horizontalArrangement = Arrangement.spacedBy(8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + PlanStatusBadge(planName = planName) + Icon( + imageVector = AppIcons.ChevronRight, + contentDescription = "查看详情", + tint = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.size(20.dp) + ) + } + } + } +} diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/PersonalInfoScreen.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/PersonalInfoScreen.kt new file mode 100644 index 0000000..1d74a04 --- /dev/null +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/PersonalInfoScreen.kt @@ -0,0 +1,195 @@ +package com.huaga.life_echo.ui.screens + +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.foundation.verticalScroll +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.automirrored.filled.ArrowBack +import androidx.compose.material3.* +import androidx.compose.runtime.Composable +import androidx.compose.runtime.collectAsState +import androidx.compose.runtime.getValue +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.sp +import androidx.lifecycle.viewmodel.compose.viewModel +import com.huaga.life_echo.ui.components.profile.PlanStatusBadge +import com.huaga.life_echo.ui.theme.LightPurple +import com.huaga.life_echo.ui.viewmodel.AuthViewModel +import com.huaga.life_echo.ui.viewmodel.PaymentViewModel +import com.huaga.life_echo.ui.viewmodel.ProfileViewModel +import com.huaga.life_echo.ui.viewmodel.ViewModelFactory +import androidx.compose.ui.platform.LocalContext + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun PersonalInfoScreen( + navController: androidx.navigation.NavHostController? = null, + authViewModel: AuthViewModel = viewModel(factory = ViewModelFactory(LocalContext.current)), + profileViewModel: ProfileViewModel = viewModel(factory = ViewModelFactory(LocalContext.current)), + paymentViewModel: PaymentViewModel = viewModel(factory = ViewModelFactory(LocalContext.current)) +) { + val currentUser by authViewModel.currentUser.collectAsState() + val userProfile by profileViewModel.userProfile.collectAsState() + val currentPlan by paymentViewModel.currentPlan.collectAsState() + + Scaffold( + topBar = { + TopAppBar( + title = { Text("个人信息") }, + navigationIcon = { + IconButton(onClick = { navController?.popBackStack() }) { + Icon( + imageVector = Icons.AutoMirrored.Filled.ArrowBack, + contentDescription = "返回" + ) + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = MaterialTheme.colorScheme.surface + ) + ) + } + ) { paddingValues -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .verticalScroll(rememberScrollState()) + .padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + // 个人信息卡片 + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(12.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surface + ) + ) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + // 昵称 + InfoRow( + label = "昵称", + value = userProfile?.nickname ?: currentUser?.nickname ?: "未设置" + ) + + HorizontalDivider() + + // 手机号 + InfoRow( + label = "手机号", + value = currentUser?.phone ?: "未设置" + ) + + // 邮箱(如果有) + if (!currentUser?.email.isNullOrBlank()) { + HorizontalDivider() + InfoRow( + label = "邮箱", + value = currentUser?.email ?: "未设置" + ) + } + } + } + + // 套餐信息卡片 + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(12.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surface + ) + ) { + Column( + modifier = Modifier.padding(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + Text( + text = "当前套餐", + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onSurface + ) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + PlanStatusBadge( + planName = currentPlan?.displayName ?: when (currentUser?.subscription_type) { + "free" -> "免费体验版" + "premium" -> "高级版" + "professional" -> "专业版" + else -> "免费体验版" + } + ) + } + + // 套餐详情(如果有) + currentPlan?.let { plan -> + HorizontalDivider() + Column( + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + text = "套餐详情", + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + plan.features.forEach { feature -> + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + text = "•", + fontSize = 14.sp, + color = LightPurple + ) + Text( + text = feature, + fontSize = 14.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + } + } + } + } + } + } +} + +@Composable +private fun InfoRow( + label: String, + value: String +) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = label, + fontSize = 14.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + Text( + text = value, + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + color = MaterialTheme.colorScheme.onSurface + ) + } +}