From bf9f3cf3636da7441fa450e17e9d43644f3d4bb4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E5=BE=90=E5=9C=A8=E5=9D=A4?= Date: Sat, 17 Jan 2026 19:35:09 +0800 Subject: [PATCH] =?UTF-8?q?=E6=B7=BB=E5=8A=A0=E5=AF=B9=E8=AF=9D=E5=88=97?= =?UTF-8?q?=E8=A1=A8=E3=80=81=E5=AF=BC=E5=87=BA=E6=95=B0=E6=8D=AE=E3=80=81?= =?UTF-8?q?=E8=AE=A2=E5=8D=95=E5=92=8C=E5=8D=87=E7=BA=A7=E8=AE=A1=E5=88=92?= =?UTF-8?q?=E7=AD=89=E6=96=B0=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/huaga/life_echo/ui/icons/AppIcons.kt | 72 ++++++ .../ui/screens/ConversationListScreen.kt | 187 ++++++++++++++++ .../life_echo/ui/screens/ExportDataScreen.kt | 191 ++++++++++++++++ .../life_echo/ui/screens/MyOrdersScreen.kt | 177 +++++++++++++++ .../life_echo/ui/screens/UpgradePlanScreen.kt | 205 ++++++++++++++++++ .../life_echo/ui/settings/AppSettings.kt | 50 +++++ .../ui/viewmodel/ConversationListViewModel.kt | 19 ++ 7 files changed, 901 insertions(+) create mode 100644 app-android/app/src/main/java/com/huaga/life_echo/ui/icons/AppIcons.kt create mode 100644 app-android/app/src/main/java/com/huaga/life_echo/ui/screens/ConversationListScreen.kt create mode 100644 app-android/app/src/main/java/com/huaga/life_echo/ui/screens/ExportDataScreen.kt create mode 100644 app-android/app/src/main/java/com/huaga/life_echo/ui/screens/MyOrdersScreen.kt create mode 100644 app-android/app/src/main/java/com/huaga/life_echo/ui/screens/UpgradePlanScreen.kt create mode 100644 app-android/app/src/main/java/com/huaga/life_echo/ui/settings/AppSettings.kt create mode 100644 app-android/app/src/main/java/com/huaga/life_echo/ui/viewmodel/ConversationListViewModel.kt diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/icons/AppIcons.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/icons/AppIcons.kt new file mode 100644 index 0000000..4fc6c85 --- /dev/null +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/icons/AppIcons.kt @@ -0,0 +1,72 @@ +package com.huaga.life_echo.ui.icons + +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.* +import androidx.compose.material.icons.outlined.* +import androidx.compose.ui.graphics.vector.ImageVector + +/** + * 应用图标常量文件 + * 统一管理应用中使用的所有图标 + */ +object AppIcons { + // 导航栏图标 + val Chat = Icons.Default.Chat + val Memoir = Icons.Default.MenuBook + val Profile = Icons.Default.Person + + // 聊天界面图标 + val ArrowBack = Icons.Default.ArrowBack + val Mic = Icons.Default.Mic + val MicOff = Icons.Default.MicOff + val SentimentSatisfied = Icons.Default.SentimentSatisfied + val Add = Icons.Default.Add + val Send = Icons.Default.Send + val Book = Icons.Default.MenuBook + + // 对话列表图标 + val Conversation = Icons.Default.ChatBubble + val DateRange = Icons.Default.DateRange + val History = Icons.Default.History + + // 回忆录界面图标 + val Chapter = Icons.Default.Book + val Reading = Icons.Default.MenuBook + val ChevronRight = Icons.Default.ChevronRight + val Edit = Icons.Default.Edit + val Share = Icons.Default.Share + val Download = Icons.Default.Download + + // 个人设置界面图标 + val Person = Icons.Default.Person + val Upgrade = Icons.Default.Star + val Receipt = Icons.Default.Receipt + val FileDownload = Icons.Default.FileDownload + val AccessTime = Icons.Default.AccessTime + val FormatSize = Icons.Default.FormatSize + val Brightness2 = Icons.Default.Brightness2 + val Settings = Icons.Default.Settings + val Help = Icons.Default.Help + val Info = Icons.Default.Info + + // 其他常用图标 + val Check = Icons.Default.Check + val Close = Icons.Default.Close + val Delete = Icons.Default.Delete + val MoreVert = Icons.Default.MoreVert + val Search = Icons.Default.Search + val Filter = Icons.Default.FilterList + val Refresh = Icons.Default.Refresh + val Favorite = Icons.Default.Favorite + val FavoriteBorder = Icons.Default.FavoriteBorder + val Star = Icons.Default.Star + val StarBorder = Icons.Default.StarBorder + + // 状态图标 + val Online = Icons.Default.Circle + val Offline = Icons.Outlined.Circle + val CheckCircle = Icons.Default.CheckCircle + val Error = Icons.Default.Error + val Warning = Icons.Default.Warning + val InfoCircle = Icons.Default.Info +} diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/ConversationListScreen.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/ConversationListScreen.kt new file mode 100644 index 0000000..37bb5ce --- /dev/null +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/ConversationListScreen.kt @@ -0,0 +1,187 @@ +package com.huaga.life_echo.ui.screens + +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.lazy.items +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material3.* +import com.huaga.life_echo.ui.icons.AppIcons +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.draw.clip +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.platform.LocalContext +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.data.database.Conversation +import com.huaga.life_echo.ui.theme.LightPurple +import com.huaga.life_echo.ui.viewmodel.ConversationListViewModel +import com.huaga.life_echo.ui.viewmodel.ViewModelFactory +import java.text.SimpleDateFormat +import java.util.* + +@Composable +fun ConversationListScreen( + onConversationClick: (String) -> Unit = {}, + viewModel: ConversationListViewModel = viewModel( + factory = ViewModelFactory(LocalContext.current) + ) +) { + val conversations by viewModel.conversations.collectAsState(initial = emptyList()) + + Column( + modifier = Modifier.fillMaxSize() + ) { + // 浅紫色标题栏 + Surface( + modifier = Modifier.fillMaxWidth(), + color = LightPurple + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .windowInsetsPadding(WindowInsets.statusBars) + .padding(top = 16.dp, bottom = 24.dp, start = 16.dp, end = 16.dp), + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "往事拾遗", + fontSize = 24.sp, + fontWeight = FontWeight.Bold, + color = Color.White + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = "用对话,留住珍贵的记忆", + fontSize = 14.sp, + color = Color.White.copy(alpha = 0.9f) + ) + } + } + + // 对话列表区域 + Column( + modifier = Modifier + .fillMaxSize() + .background(MaterialTheme.colorScheme.background) + ) { + Text( + text = "我的对话", + modifier = Modifier.padding(16.dp, 16.dp, 16.dp, 8.dp), + fontSize = 16.sp, + fontWeight = FontWeight.Medium, + color = MaterialTheme.colorScheme.onSurface + ) + + if (conversations.isEmpty()) { + // 空状态 - 显示示例对话项 + ConversationItem( + conversation = Conversation( + id = "demo", + userId = "user", + startedAt = System.currentTimeMillis(), + endedAt = null, + durationSeconds = 0, + summary = "您想从哪里开始呢?可以聊聊童年...", + currentTopic = null, + conversationStage = null + ), + onClick = { onConversationClick("demo") } + ) + } else { + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues(horizontal = 16.dp, vertical = 8.dp), + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + items(conversations) { conversation -> + ConversationItem( + conversation = conversation, + onClick = { onConversationClick(conversation.id) } + ) + } + } + } + } + } +} + +@Composable +fun ConversationItem( + conversation: Conversation, + onClick: () -> Unit +) { + val timeText = if (conversation.endedAt != null) { + val date = Date(conversation.endedAt) + val now = Date() + val diff = now.time - date.time + when { + diff < 60000 -> "刚刚" + diff < 3600000 -> "${diff / 60000}分钟前" + diff < 86400000 -> "${diff / 3600000}小时前" + else -> SimpleDateFormat("MM月dd日", Locale.getDefault()).format(date) + } + } else { + "刚刚" + } + + Row( + modifier = Modifier + .fillMaxWidth() + .clickable { onClick() } + .padding(vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + // 浅紫色图标背景 + Box( + modifier = Modifier + .size(48.dp) + .clip(RoundedCornerShape(8.dp)) + .background(LightPurple), + contentAlignment = Alignment.Center + ) { + Icon( + imageVector = AppIcons.Conversation, + contentDescription = "对话", + tint = Color.White, + modifier = Modifier.size(24.dp) + ) + } + + Spacer(modifier = Modifier.width(12.dp)) + + // 对话信息 + Column( + modifier = Modifier.weight(1f) + ) { + Text( + text = "回忆录助手", + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onSurface + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = conversation.summary ?: conversation.currentTopic ?: "您想从哪里开始呢?可以聊聊童年...", + fontSize = 14.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant, + maxLines = 1 + ) + } + + // 时间戳 + Text( + text = timeText, + fontSize = 12.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.padding(start = 8.dp) + ) + } +} diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/ExportDataScreen.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/ExportDataScreen.kt new file mode 100644 index 0000000..7491977 --- /dev/null +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/ExportDataScreen.kt @@ -0,0 +1,191 @@ +package com.huaga.life_echo.ui.screens + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.CheckCircle +import androidx.compose.material3.* +import androidx.compose.runtime.* +import androidx.compose.runtime.rememberCoroutineScope +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +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 +import com.huaga.life_echo.ui.theme.LightPurple +import kotlinx.coroutines.delay +import kotlinx.coroutines.launch + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun ExportDataScreen( + navController: androidx.navigation.NavHostController? = null +) { + var isExporting by remember { mutableStateOf(false) } + var exportCompleted by remember { mutableStateOf(false) } + val scope = rememberCoroutineScope() + + Scaffold( + topBar = { + TopAppBar( + title = { Text("导出所有数据") }, + navigationIcon = { + IconButton(onClick = { navController?.popBackStack() }) { + Icon( + imageVector = AppIcons.ArrowBack, + contentDescription = "返回" + ) + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = LightPurple, + titleContentColor = Color.White, + navigationIconContentColor = Color.White + ) + ) + } + ) { paddingValues -> + Column( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .background(MaterialTheme.colorScheme.background) + .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 + .fillMaxWidth() + .padding(16.dp) + ) { + Text( + text = "导出说明", + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onSurface + ) + Spacer(modifier = Modifier.height(12.dp)) + Text( + text = "导出数据将包含:", + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = MaterialTheme.colorScheme.onSurface + ) + Spacer(modifier = Modifier.height(8.dp)) + listOf( + "所有对话记录", + "回忆录内容", + "章节信息", + "用户设置" + ).forEach { item -> + Row( + modifier = Modifier.padding(vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "• ", + fontSize = 14.sp, + color = LightPurple, + fontWeight = FontWeight.Bold + ) + Text( + text = item, + fontSize = 14.sp, + color = MaterialTheme.colorScheme.onSurface + ) + } + } + } + } + + // 导出格式选择 + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(12.dp), + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Text( + text = "选择导出格式", + fontSize = 16.sp, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onSurface + ) + Spacer(modifier = Modifier.height(12.dp)) + listOf("JSON", "CSV", "PDF").forEach { format -> + Row( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 8.dp), + verticalAlignment = Alignment.CenterVertically + ) { + RadioButton( + selected = false, // TODO: 管理选中状态 + onClick = { /* TODO */ } + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + text = format, + fontSize = 14.sp, + color = MaterialTheme.colorScheme.onSurface + ) + } + } + } + } + + Spacer(modifier = Modifier.weight(1f)) + + // 导出按钮 + Button( + onClick = { + isExporting = true + // TODO: 执行导出操作 + // 模拟导出完成 + scope.launch { + delay(2000) + isExporting = false + exportCompleted = true + } + }, + modifier = Modifier.fillMaxWidth(), + enabled = !isExporting && !exportCompleted, + colors = ButtonDefaults.buttonColors( + containerColor = LightPurple + ) + ) { + if (isExporting) { + CircularProgressIndicator( + modifier = Modifier.size(20.dp), + color = Color.White + ) + Spacer(modifier = Modifier.width(8.dp)) + Text("导出中...", color = Color.White) + } else if (exportCompleted) { + Icon( + imageVector = Icons.Default.CheckCircle, + contentDescription = "完成", + tint = Color.White + ) + Spacer(modifier = Modifier.width(8.dp)) + Text("导出完成", color = Color.White) + } else { + Text("开始导出", color = Color.White) + } + } + } + } +} diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/MyOrdersScreen.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/MyOrdersScreen.kt new file mode 100644 index 0000000..e9bff1a --- /dev/null +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/MyOrdersScreen.kt @@ -0,0 +1,177 @@ +package com.huaga.life_echo.ui.screens + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +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.graphics.Color +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 +import com.huaga.life_echo.ui.theme.LightPurple + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun MyOrdersScreen( + navController: androidx.navigation.NavHostController? = null +) { + Scaffold( + topBar = { + TopAppBar( + title = { Text("我的订单") }, + navigationIcon = { + IconButton(onClick = { navController?.popBackStack() }) { + Icon( + imageVector = AppIcons.ArrowBack, + contentDescription = "返回" + ) + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = LightPurple, + titleContentColor = Color.White, + navigationIconContentColor = Color.White + ) + ) + } + ) { paddingValues -> + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .background(MaterialTheme.colorScheme.background), + contentPadding = PaddingValues(16.dp), + verticalArrangement = Arrangement.spacedBy(12.dp) + ) { + // 示例订单 + item { + OrderCard( + orderId = "ORD20240101001", + planName = "高级版", + price = "¥29", + date = "2024年1月1日", + status = "已完成" + ) + } + + item { + OrderCard( + orderId = "ORD20231215002", + planName = "专业版", + price = "¥99", + date = "2023年12月15日", + status = "已完成" + ) + } + + // 空状态提示 + if (false) { // 当没有订单时显示 + item { + Box( + modifier = Modifier + .fillMaxWidth() + .padding(vertical = 64.dp), + contentAlignment = Alignment.Center + ) { + Column( + horizontalAlignment = Alignment.CenterHorizontally + ) { + Text( + text = "暂无订单", + fontSize = 18.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + Spacer(modifier = Modifier.height(8.dp)) + Text( + text = "升级套餐后,订单将显示在这里", + fontSize = 14.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + } + } + } + } + } + } +} + +@Composable +fun OrderCard( + orderId: String, + planName: String, + price: String, + date: String, + status: String +) { + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(12.dp), + colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surface) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = planName, + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onSurface + ) + Text( + text = status, + fontSize = 14.sp, + color = LightPurple, + fontWeight = FontWeight.Medium + ) + } + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = "订单号: $orderId", + fontSize = 12.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + + Spacer(modifier = Modifier.height(4.dp)) + + Text( + text = "日期: $date", + fontSize = 12.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + + Spacer(modifier = Modifier.height(8.dp)) + + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "金额", + fontSize = 14.sp, + color = MaterialTheme.colorScheme.onSurface + ) + Text( + text = price, + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + color = LightPurple + ) + } + } + } +} diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/UpgradePlanScreen.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/UpgradePlanScreen.kt new file mode 100644 index 0000000..404c421 --- /dev/null +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/UpgradePlanScreen.kt @@ -0,0 +1,205 @@ +package com.huaga.life_echo.ui.screens + +import androidx.compose.foundation.background +import androidx.compose.foundation.layout.* +import androidx.compose.foundation.lazy.LazyColumn +import androidx.compose.foundation.shape.RoundedCornerShape +import androidx.compose.material.icons.Icons +import androidx.compose.material.icons.filled.ArrowBack +import androidx.compose.material.icons.filled.CheckCircle +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.graphics.Color +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 +import com.huaga.life_echo.ui.theme.LightPurple + +@OptIn(ExperimentalMaterial3Api::class) +@Composable +fun UpgradePlanScreen( + navController: androidx.navigation.NavHostController? = null +) { + Scaffold( + topBar = { + TopAppBar( + title = { Text("升级套餐") }, + navigationIcon = { + IconButton(onClick = { navController?.popBackStack() }) { + Icon( + imageVector = AppIcons.ArrowBack, + contentDescription = "返回" + ) + } + }, + colors = TopAppBarDefaults.topAppBarColors( + containerColor = LightPurple, + titleContentColor = Color.White, + navigationIconContentColor = Color.White + ) + ) + } + ) { paddingValues -> + LazyColumn( + modifier = Modifier + .fillMaxSize() + .padding(paddingValues) + .background(MaterialTheme.colorScheme.background), + contentPadding = PaddingValues(16.dp), + verticalArrangement = Arrangement.spacedBy(16.dp) + ) { + item { + Text( + text = "选择套餐", + fontSize = 24.sp, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onSurface + ) + } + + // 免费版 + item { + PlanCard( + title = "免费体验版", + price = "免费", + features = listOf( + "基础对话功能", + "每月3次对话", + "基础回忆录整理" + ), + isSelected = true, + onClick = { } + ) + } + + // 高级版 + item { + PlanCard( + title = "高级版", + price = "¥29/月", + features = listOf( + "无限对话次数", + "完整回忆录导出", + "PDF格式导出", + "优先客服支持", + "数据云端同步" + ), + isSelected = false, + onClick = { /* TODO: 购买 */ } + ) + } + + // 专业版 + item { + PlanCard( + title = "专业版", + price = "¥99/月", + features = listOf( + "高级版所有功能", + "AI智能整理", + "多格式导出(PDF、Word、EPUB)", + "专属客服支持", + "无限云端存储", + "多设备同步" + ), + isSelected = false, + onClick = { /* TODO: 购买 */ } + ) + } + } + } +} + +@Composable +fun PlanCard( + title: String, + price: String, + features: List, + isSelected: Boolean, + onClick: () -> Unit +) { + Card( + modifier = Modifier.fillMaxWidth(), + shape = RoundedCornerShape(12.dp), + colors = CardDefaults.cardColors( + containerColor = if (isSelected) LightPurple.copy(alpha = 0.1f) else MaterialTheme.colorScheme.surface + ), + border = if (isSelected) { + androidx.compose.foundation.BorderStroke(2.dp, LightPurple) + } else null + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(20.dp) + ) { + Row( + modifier = Modifier.fillMaxWidth(), + horizontalArrangement = Arrangement.SpaceBetween, + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = title, + fontSize = 20.sp, + fontWeight = FontWeight.Bold, + color = MaterialTheme.colorScheme.onSurface + ) + if (isSelected) { + Icon( + imageVector = Icons.Default.CheckCircle, + contentDescription = "当前套餐", + tint = LightPurple, + modifier = Modifier.size(24.dp) + ) + } + } + + Spacer(modifier = Modifier.height(8.dp)) + + Text( + text = price, + fontSize = 18.sp, + fontWeight = FontWeight.Bold, + color = LightPurple + ) + + Spacer(modifier = Modifier.height(16.dp)) + + features.forEach { feature -> + Row( + modifier = Modifier.padding(vertical = 4.dp), + verticalAlignment = Alignment.CenterVertically + ) { + Text( + text = "• ", + fontSize = 16.sp, + color = LightPurple, + fontWeight = FontWeight.Bold + ) + Text( + text = feature, + fontSize = 14.sp, + color = MaterialTheme.colorScheme.onSurface + ) + } + } + + if (!isSelected) { + Spacer(modifier = Modifier.height(16.dp)) + Button( + onClick = onClick, + modifier = Modifier.fillMaxWidth(), + colors = ButtonDefaults.buttonColors( + containerColor = LightPurple + ) + ) { + Text("立即升级", color = Color.White) + } + } + } + } +} diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/settings/AppSettings.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/settings/AppSettings.kt new file mode 100644 index 0000000..59c655b --- /dev/null +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/settings/AppSettings.kt @@ -0,0 +1,50 @@ +package com.huaga.life_echo.ui.settings + +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.mutableStateOf +import androidx.compose.runtime.remember +import androidx.compose.runtime.setValue + +/** + * 应用设置管理 + */ +object AppSettings { + // 语速设置:慢速、标准、快速 + enum class SpeechRate(val label: String, val multiplier: Float) { + SLOW("慢速", 0.75f), + STANDARD("标准", 1.0f), + FAST("快速", 1.5f) + } + + private val _speechRate = mutableStateOf(SpeechRate.STANDARD) + var speechRate: SpeechRate + get() = _speechRate.value + set(value) { _speechRate.value = value } + + private val _largeFontMode = mutableStateOf(false) + var largeFontMode: Boolean + get() = _largeFontMode.value + set(value) { _largeFontMode.value = value } + + private val _darkMode = mutableStateOf(false) + var darkMode: Boolean + get() = _darkMode.value + set(value) { _darkMode.value = value } + + // 用于在Compose中观察设置变化 - 直接返回mutableStateOf的值 + @Composable + fun rememberDarkMode(): Boolean { + return remember { _darkMode }.value + } + + @Composable + fun rememberLargeFontMode(): Boolean { + return remember { _largeFontMode }.value + } + + @Composable + fun rememberSpeechRate(): SpeechRate { + return remember { _speechRate }.value + } +} diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/viewmodel/ConversationListViewModel.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/viewmodel/ConversationListViewModel.kt new file mode 100644 index 0000000..0160b4b --- /dev/null +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/viewmodel/ConversationListViewModel.kt @@ -0,0 +1,19 @@ +package com.huaga.life_echo.ui.viewmodel + +import androidx.lifecycle.ViewModel +import androidx.lifecycle.viewModelScope +import com.huaga.life_echo.data.repository.ConversationRepository +import kotlinx.coroutines.flow.SharingStarted +import kotlinx.coroutines.flow.stateIn + +class ConversationListViewModel( + conversationRepository: ConversationRepository +) : ViewModel() { + + val conversations = conversationRepository.getAllConversations() + .stateIn( + scope = viewModelScope, + started = SharingStarted.WhileSubscribed(5000), + initialValue = emptyList() + ) +}