From 2592143789bb102e548c27e51d081b877a7251ff Mon Sep 17 00:00:00 2001 From: iammm0 Date: Fri, 23 Jan 2026 14:02:50 +0800 Subject: [PATCH] =?UTF-8?q?refactor:=20=E4=BC=98=E5=8C=96=E5=89=8D?= =?UTF-8?q?=E7=AB=AF=E5=8A=9F=E8=83=BD=E5=B1=8F=E5=B9=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 优化ConversationListScreen对话列表页面 - 优化CreateMemoryScreen创建回忆页面 - 优化MyMemoirScreen我的回忆录页面 - 优化ProfileScreen个人资料页面 - 优化ExportDataScreen导出数据页面 - 优化AboutScreen关于页面 --- .../huaga/life_echo/ui/screens/AboutScreen.kt | 2 +- .../ui/screens/ConversationListScreen.kt | 72 ++-- .../ui/screens/CreateMemoryScreen.kt | 73 +++- .../life_echo/ui/screens/ExportDataScreen.kt | 54 ++- .../life_echo/ui/screens/MyMemoirScreen.kt | 343 +++++++++++++----- .../life_echo/ui/screens/ProfileScreen.kt | 142 ++++---- 6 files changed, 459 insertions(+), 227 deletions(-) diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/AboutScreen.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/AboutScreen.kt index bf66d25..bdeeab1 100644 --- a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/AboutScreen.kt +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/AboutScreen.kt @@ -160,7 +160,7 @@ fun AboutScreen( // 版权信息 Text( - text = "© 2024 Life Echo. All rights reserved.", + text = "© 2026 Life Echo. All rights reserved.", fontSize = 14.sp, color = MaterialTheme.colorScheme.onSurfaceVariant, modifier = Modifier.padding(top = 16.dp) 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 index 99678af..4e2c7ab 100644 --- 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 @@ -1,22 +1,26 @@ package com.huaga.life_echo.ui.screens import androidx.compose.foundation.background -import androidx.compose.foundation.layout.* +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.PaddingValues +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.padding import androidx.compose.foundation.lazy.LazyColumn import androidx.compose.foundation.lazy.items -import androidx.compose.material3.* +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.Text import androidx.compose.runtime.Composable import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.collectAsState import androidx.compose.runtime.getValue -import androidx.compose.ui.Alignment +import androidx.compose.runtime.rememberCoroutineScope import androidx.compose.ui.Modifier 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.network.models.ConversationListItemDto import com.huaga.life_echo.ui.components.common.EmptyStateView import com.huaga.life_echo.ui.components.common.LoadingIndicator @@ -24,6 +28,7 @@ import com.huaga.life_echo.ui.components.conversation.ConversationListHeader import com.huaga.life_echo.ui.components.conversation.ConversationListItem import com.huaga.life_echo.ui.viewmodel.ConversationListViewModel import com.huaga.life_echo.ui.viewmodel.ViewModelFactory +import kotlinx.coroutines.launch @Composable fun ConversationListScreen( @@ -41,11 +46,26 @@ fun ConversationListScreen( viewModel.refreshConversations() } + val scope = rememberCoroutineScope() + + // 处理新建对话 + val handleCreateConversation: () -> Unit = { + scope.launch { + val result = viewModel.createConversation() + result.fold( + onSuccess = { conversationId -> + onConversationClick(conversationId) + }, + onFailure = { exception -> + // 错误处理可以在这里添加 + } + ) + } + } + Column( modifier = Modifier.fillMaxSize() ) { - // 使用新的头部组件 - ConversationListHeader() // 对话列表区域 Column( @@ -53,6 +73,7 @@ fun ConversationListScreen( .fillMaxSize() .background(MaterialTheme.colorScheme.background) ) { + ConversationListHeader(onCreateConversation = handleCreateConversation) Text( text = "我的对话", modifier = Modifier.padding(16.dp, 16.dp, 16.dp, 8.dp), @@ -66,12 +87,20 @@ fun ConversationListScreen( LoadingIndicator() } error != null -> { - // 即使有错误,也显示默认对话 - DefaultConversationItem(onClick = { onConversationClick("demo") }) + // 显示错误信息 + EmptyStateView( + title = "加载失败", + message = error ?: "未知错误", + modifier = Modifier.fillMaxSize() + ) } conversations.isEmpty() -> { - // 空状态 - 显示默认对话 - DefaultConversationItem(onClick = { onConversationClick("demo") }) + // 空状态 - 提示用户创建新对话 + EmptyStateView( + title = "还没有对话", + message = "点击上方「新建对话」按钮开始您的回忆录之旅", + modifier = Modifier.fillMaxSize() + ) } else -> { LazyColumn( @@ -92,7 +121,12 @@ fun ConversationListScreen( ) ConversationListItem( conversation = dto, - onClick = { onConversationClick(conversation.id) } + onClick = { onConversationClick(conversation.id) }, + onDelete = { + scope.launch { + viewModel.deleteConversation(conversation.id) + } + } ) } } @@ -102,19 +136,3 @@ fun ConversationListScreen( } } -@Composable -private fun DefaultConversationItem(onClick: () -> Unit) { - val defaultDto = ConversationListItemDto( - id = "demo", - title = "回忆录助手", - avatarUrl = null, - latestMessagePreview = "您想从哪里开始呢?可以聊聊童年...", - latestMessageTime = System.currentTimeMillis(), - unreadCount = 0, - isDefaultAssistant = true - ) - ConversationListItem( - conversation = defaultDto, - onClick = onClick - ) -} diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/CreateMemoryScreen.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/CreateMemoryScreen.kt index 144baeb..cfce573 100644 --- a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/CreateMemoryScreen.kt +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/CreateMemoryScreen.kt @@ -19,6 +19,7 @@ import androidx.lifecycle.viewmodel.compose.viewModel import com.huaga.life_echo.data.database.Message import com.huaga.life_echo.network.models.MessageDto import com.huaga.life_echo.ui.components.chat.* +import com.huaga.life_echo.ui.components.debug.WebSocketDebugPanel import com.huaga.life_echo.ui.theme.LightPurple import com.huaga.life_echo.ui.viewmodel.CreateMemoryViewModel import com.huaga.life_echo.ui.viewmodel.ViewModelFactory @@ -39,32 +40,58 @@ fun CreateMemoryScreen( val agentResponse by viewModel.agentResponse.collectAsState() val connectionStatus by viewModel.connectionStatus.collectAsState() val userMessages by viewModel.userMessages.collectAsState() + val historyMessages by viewModel.historyMessages.collectAsState() val isStreaming by viewModel.isStreaming.collectAsState() val streamingText by viewModel.streamingText.collectAsState() val isTyping by viewModel.isTyping.collectAsState() + // 调试信息 + val wsIsConnected by viewModel.wsIsConnected.collectAsState() + val lastMessageType by viewModel.lastMessageType.collectAsState() + val lastMessageTime by viewModel.lastMessageTime.collectAsState() + val errorMessages by viewModel.errorMessages.collectAsState() + val messageCount by viewModel.messageCount.collectAsState() + val conversationIdState by viewModel.conversationId.collectAsState() + + // 初始化对话 + LaunchedEffect(conversationId) { + if (conversationId != "new") { + viewModel.initializeConversation(conversationId) + } else { + // 如果是新建对话,启动新对话 + viewModel.startConversation() + } + } + // 输入框状态 var inputText by remember { mutableStateOf("") } val keyboardController = LocalSoftwareKeyboardController.current - // 构建消息列表 - val messages = remember(userMessages, agentResponse) { + // 构建消息列表(包含历史消息和当前消息) + val messages = remember(historyMessages, userMessages, agentResponse) { buildList { - // 添加用户消息 + // 先添加历史消息 + addAll(historyMessages) + + // 添加当前会话的用户消息(排除已存在的) + val existingUserMessageIds = historyMessages.filter { it.senderType == "user" }.map { it.content }.toSet() userMessages.forEachIndexed { index, text -> - add(MessageDto( - id = "user_$index", - conversationId = conversationId, - content = text, - senderType = "user", - timestamp = System.currentTimeMillis() - (userMessages.size - index) * 1000L, - messageType = "text" - )) + if (!existingUserMessageIds.contains(text)) { + add(MessageDto( + id = "user_${historyMessages.size + index}", + conversationId = conversationId, + content = text, + senderType = "user", + timestamp = System.currentTimeMillis() - (userMessages.size - index) * 1000L, + messageType = "text" + )) + } } - // 添加AI回复 - if (agentResponse.isNotEmpty()) { + + // 添加AI回复(如果不在历史消息中) + if (agentResponse.isNotEmpty() && !historyMessages.any { it.content == agentResponse && it.senderType == "assistant" }) { add(MessageDto( - id = "ai_response", + id = "ai_response_${historyMessages.size + userMessages.size}", conversationId = conversationId, content = agentResponse, senderType = "assistant", @@ -72,7 +99,7 @@ fun CreateMemoryScreen( messageType = "text" )) } - } + }.sortedBy { it.timestamp } // 按时间排序 } Column( @@ -85,6 +112,22 @@ fun CreateMemoryScreen( onBackClick = { navController?.popBackStack() } ) + // WebSocket调试面板(开发测试用) + WebSocketDebugPanel( + connectionStatus = connectionStatus, + conversationId = conversationIdState, + isConnected = wsIsConnected, + isStreaming = isStreaming, + isTyping = isTyping, + lastMessageType = lastMessageType, + lastMessageTime = lastMessageTime, + errorMessages = errorMessages, + messageCount = messageCount, + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp) + ) + // 使用新的MessageList组件(包含所有消息、流式内容和输入指示器) MessageList( messages = messages, 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 index 7491977..2798600 100644 --- 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 @@ -149,42 +149,40 @@ fun ExportDataScreen( Spacer(modifier = Modifier.weight(1f)) - // 导出按钮 + // 导出按钮 - 显示开发中提示 + var showDevDialog by remember { mutableStateOf(false) } + Button( onClick = { - isExporting = true - // TODO: 执行导出操作 - // 模拟导出完成 - scope.launch { - delay(2000) - isExporting = false - exportCompleted = true - } + showDevDialog = 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) - } + Text("开始导出", color = Color.White) + } + + // 开发中提示对话框 + if (showDevDialog) { + AlertDialog( + onDismissRequest = { showDevDialog = false }, + title = { Text("功能开发中") }, + text = { + Text( + text = "导出所有数据功能正在开发中,敬请期待!", + modifier = Modifier.wrapContentHeight() + ) + }, + confirmButton = { + TextButton( + onClick = { showDevDialog = false } + ) { + Text("确定", color = LightPurple) + } + } + ) } } } diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/MyMemoirScreen.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/MyMemoirScreen.kt index 28e0ef9..fc2c623 100644 --- a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/MyMemoirScreen.kt +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/MyMemoirScreen.kt @@ -1,5 +1,7 @@ package com.huaga.life_echo.ui.screens +import android.os.Build +import androidx.annotation.RequiresApi import androidx.compose.animation.* import androidx.compose.animation.core.tween import androidx.compose.foundation.background @@ -18,21 +20,33 @@ 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.compose.ui.window.Dialog import androidx.lifecycle.viewmodel.compose.viewModel +import androidx.compose.foundation.lazy.rememberLazyListState +import kotlinx.coroutines.launch +import androidx.compose.material3.pulltorefresh.PullToRefreshBox import com.huaga.life_echo.data.database.Chapter import com.huaga.life_echo.network.models.ChapterContentDto import com.huaga.life_echo.network.models.ChapterDto import com.huaga.life_echo.ui.components.memoir.* +import com.huaga.life_echo.ui.components.common.EmptyStateView import com.huaga.life_echo.ui.theme.LightPurple import com.huaga.life_echo.ui.viewmodel.MyMemoirViewModel import com.huaga.life_echo.ui.viewmodel.ViewModelFactory +import com.huaga.life_echo.ui.viewmodel.ConversationListViewModel import com.huaga.life_echo.ui.icons.AppIcons +import com.huaga.life_echo.network.models.ConversationListItemDto +@OptIn(ExperimentalMaterial3Api::class) +@RequiresApi(Build.VERSION_CODES.O) @Composable fun MyMemoirScreen( navController: androidx.navigation.NavHostController? = null, viewModel: MyMemoirViewModel = viewModel( factory = ViewModelFactory(LocalContext.current) + ), + conversationListViewModel: ConversationListViewModel = viewModel( + factory = ViewModelFactory(LocalContext.current) ) ) { val chapters by viewModel.chapters.collectAsState(initial = emptyList()) @@ -40,7 +54,53 @@ fun MyMemoirScreen( val isLoading by viewModel.isLoading.collectAsState() val bookInfo by viewModel.bookInfo.collectAsState() val showFullTextReading by viewModel.showFullTextReading.collectAsState() - var selectedTab by remember { mutableStateOf(0) } + + // 整理对话相关状态 + var showOrganizeDialog by remember { mutableStateOf(false) } + var showOrganizeSuccessDialog by remember { mutableStateOf(false) } + val conversations by conversationListViewModel.conversations.collectAsState(initial = emptyList()) + val conversationsLoading by conversationListViewModel.isLoading.collectAsState() + val isOrganizing by viewModel.isOrganizing.collectAsState() + val organizingProgress by viewModel.organizingProgress.collectAsState() + val organizingStatus by viewModel.organizingStatus.collectAsState() + + // 下拉刷新状态 + var isRefreshing by remember { mutableStateOf(false) } + val refreshScope = rememberCoroutineScope() + + // 下拉刷新处理 + fun handleRefresh() { + refreshScope.launch { + isRefreshing = true + viewModel.refreshChapters() + viewModel.loadBookInfo() + // 等待刷新完成 + kotlinx.coroutines.delay(500) + isRefreshing = false + } + } + + // 加载对话列表(转换为ConversationListItemDto格式) + val conversationDtos = remember(conversations) { + conversations.map { conv -> + ConversationListItemDto( + id = conv.id, + title = conv.title ?: "回忆录助手", + avatarUrl = conv.avatarUrl, + latestMessagePreview = conv.latestMessagePreview, + latestMessageTime = conv.latestMessageTime ?: conv.startedAt, + unreadCount = 0, + isDefaultAssistant = conv.title == null + ) + } + } + + // 加载对话列表 + LaunchedEffect(showOrganizeDialog) { + if (showOrganizeDialog) { + conversationListViewModel.refreshConversations() + } + } // 加载书籍信息 LaunchedEffect(Unit) { @@ -104,31 +164,18 @@ fun MyMemoirScreen( ) } else { Column(modifier = Modifier.fillMaxSize()) { - // 顶部标签页 - Row( - modifier = Modifier - .fillMaxWidth() - .padding(horizontal = 16.dp, vertical = 8.dp) - ) { - TabButton( - text = "目录", - selected = selectedTab == 0, - onClick = { selectedTab = 0 }, - modifier = Modifier.padding(end = 24.dp) - ) - TabButton( - text = "正在阅读", - selected = selectedTab == 1, - onClick = { selectedTab = 1 } - ) - } - if (selectedChapter == null) { - // 目录视图 - LazyColumn( - modifier = Modifier.fillMaxSize(), - contentPadding = PaddingValues(horizontal = 16.dp, vertical = 16.dp) + // 目录视图(带下拉刷新) + PullToRefreshBox( + isRefreshing = isRefreshing, + onRefresh = { handleRefresh() }, + modifier = Modifier.fillMaxSize() ) { + LazyColumn( + modifier = Modifier.fillMaxSize(), + contentPadding = PaddingValues(horizontal = 16.dp, vertical = 16.dp), + state = rememberLazyListState() + ) { // 书籍信息卡片 item { bookInfo?.let { book -> @@ -160,62 +207,150 @@ fun MyMemoirScreen( } } - // 「阅读全文」浮动按钮区域 + // 操作按钮区域 item { - Row( + Column( modifier = Modifier .fillMaxWidth() - .padding(vertical = 16.dp), - horizontalArrangement = Arrangement.Center + .padding(vertical = 16.dp) ) { - FloatingActionButton( - onClick = { viewModel.toggleFullTextReading() }, - containerColor = LightPurple - ) { - Icon( - imageVector = AppIcons.Reading, - contentDescription = "阅读全文", - tint = Color.White - ) - Spacer(modifier = Modifier.width(8.dp)) - Text("阅读全文", color = Color.White) - } - } - } - - // 章节列表 - items(chapterDtos.sortedBy { it.order_index }, key = { it.id }) { chapterDto -> - ChapterCard( - chapter = chapterDto, - onClick = { - // 查找对应的Chapter实体 - chapters.find { it.id == chapterDto.id }?.let { chapter -> - viewModel.selectChapter(chapter) + // 整理进度条(如果正在整理) + if (isOrganizing) { + Card( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp, vertical = 8.dp), + shape = RoundedCornerShape(12.dp), + colors = CardDefaults.cardColors( + containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.5f) + ) + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(16.dp) + ) { + Text( + text = organizingStatus.ifEmpty { "正在整理对话..." }, + fontSize = 14.sp, + fontWeight = FontWeight.Medium, + color = MaterialTheme.colorScheme.onSurface, + modifier = Modifier.padding(bottom = 8.dp) + ) + LinearProgressIndicator( + progress = organizingProgress, + modifier = Modifier.fillMaxWidth(), + color = LightPurple, + trackColor = MaterialTheme.colorScheme.surfaceVariant + ) + Spacer(modifier = Modifier.height(4.dp)) + Text( + text = "${(organizingProgress * 100).toInt()}%", + fontSize = 12.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.align(Alignment.End) + ) + } } } - ) - Spacer(modifier = Modifier.height(12.dp)) - } - - // 如果没有章节,显示示例 - if (chapterDtos.isEmpty()) { - item { - // 显示示例章节 - val now = java.time.Instant.now().toString() - listOf( - ChapterDto("demo1", "童年与家庭", "", 1, "completed", "childhood", emptyList(), now, false, emptyList()), - ChapterDto("demo2", "上学的日子", "", 2, "partial", "education", emptyList(), now, false, emptyList()), - ChapterDto("demo3", "工作与事业", "", 3, "pending", "career", emptyList(), now, true, emptyList()), - ChapterDto("demo4", "爱情与婚姻", "", 4, "pending", "family", emptyList(), now, true, emptyList()) - ).forEach { chapterDto -> - ChapterCard( - chapter = chapterDto, - onClick = { } - ) - Spacer(modifier = Modifier.height(12.dp)) + + // 操作按钮 + Row( + modifier = Modifier + .fillMaxWidth() + .padding(horizontal = 16.dp), + horizontalArrangement = Arrangement.spacedBy(12.dp) + ) { + // 整理对话按钮(改进样式) + Button( + onClick = { showOrganizeDialog = true }, + modifier = Modifier.weight(1f), + enabled = !isOrganizing, + colors = ButtonDefaults.buttonColors( + containerColor = LightPurple + ), + shape = RoundedCornerShape(12.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Icon( + imageVector = AppIcons.Edit, + contentDescription = null, + tint = Color.White, + modifier = Modifier.size(20.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + "整理对话", + color = Color.White, + fontSize = 14.sp + ) + } + } + + // 「阅读全文」按钮 - 只在有章节时显示 + if (chapterDtos.isNotEmpty()) { + Button( + onClick = { viewModel.toggleFullTextReading() }, + modifier = Modifier.weight(1f), + colors = ButtonDefaults.buttonColors( + containerColor = LightPurple + ), + shape = RoundedCornerShape(12.dp) + ) { + Row( + verticalAlignment = Alignment.CenterVertically, + horizontalArrangement = Arrangement.Center + ) { + Icon( + imageVector = AppIcons.Reading, + contentDescription = null, + tint = Color.White, + modifier = Modifier.size(20.dp) + ) + Spacer(modifier = Modifier.width(8.dp)) + Text( + "阅读全文", + color = Color.White, + fontSize = 14.sp + ) + } + } + } } } } + + // 如果没有章节,显示空状态 + if (chapterDtos.isEmpty()) { + item { + EmptyStateView( + title = "还没有章节", + message = "开始对话,让AI帮您整理回忆录章节", + icon = "📖", + modifier = Modifier + .fillMaxWidth() + .padding(top = 48.dp) + ) + } + } else { + // 章节列表 + items(chapterDtos.sortedBy { it.order_index }, key = { it.id }) { chapterDto -> + ChapterCard( + chapter = chapterDto, + onClick = { + // 查找对应的Chapter实体 + chapters.find { it.id == chapterDto.id }?.let { chapter -> + viewModel.selectChapter(chapter) + } + } + ) + Spacer(modifier = Modifier.height(12.dp)) + } + } + } } } else { // 章节阅读视图 @@ -261,30 +396,48 @@ fun MyMemoirScreen( } } } - } -} - -@Composable -fun TabButton( - text: String, - selected: Boolean, - onClick: () -> Unit, - modifier: Modifier = Modifier -) { - Column(modifier = modifier.clickable { onClick() }) { - Text( - text = text, - fontSize = 16.sp, - fontWeight = if (selected) FontWeight.Bold else FontWeight.Normal, - color = if (selected) MaterialTheme.colorScheme.primary else MaterialTheme.colorScheme.onSurfaceVariant - ) - if (selected) { - Spacer(modifier = Modifier.height(4.dp)) - Box( - modifier = Modifier - .height(2.dp) - .fillMaxWidth() - .background(LightPurple) + + // 整理对话对话框 + if (showOrganizeDialog) { + OrganizeConversationDialog( + conversations = conversationDtos, + isLoading = conversationsLoading, + onDismiss = { showOrganizeDialog = false }, + onSelectConversation = { conversationId -> + viewModel.organizeConversation( + conversationId = conversationId, + onSuccess = { + showOrganizeSuccessDialog = true + }, + onError = { errorMsg -> + // 错误处理可以在这里添加 + } + ) + } + ) + } + + // 整理成功对话框 + if (showOrganizeSuccessDialog) { + AlertDialog( + onDismissRequest = { showOrganizeSuccessDialog = false }, + title = { Text("整理成功") }, + text = { + Text( + text = "对话内容正在整理中,请稍后刷新查看章节。", + modifier = Modifier.wrapContentHeight() + ) + }, + confirmButton = { + TextButton( + onClick = { + showOrganizeSuccessDialog = false + viewModel.refreshChapters() + } + ) { + Text("确定", color = LightPurple) + } + } ) } } diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/ProfileScreen.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/ProfileScreen.kt index 57d55d7..d4627a6 100644 --- a/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/ProfileScreen.kt +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/screens/ProfileScreen.kt @@ -58,6 +58,9 @@ 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 +import androidx.activity.compose.rememberLauncherForActivityResult +import androidx.activity.result.contract.ActivityResultContracts +import java.io.File @Composable fun ProfileScreen( @@ -73,11 +76,30 @@ fun ProfileScreen( var speechRate by remember { mutableStateOf(AppSettings.speechRate) } var showSpeechRateDialog by remember { mutableStateOf(false) } var showLogoutDialog by remember { mutableStateOf(false) } + var showUpgradePlanDialog by remember { mutableStateOf(false) } val isLoggedIn by authViewModel.isLoggedIn.collectAsState() val currentUser by authViewModel.currentUser.collectAsState() val userProfile by profileViewModel.userProfile.collectAsState() val currentPlan by paymentViewModel.currentPlan.collectAsState() + val isLoading by authViewModel.isLoading.collectAsState() + + // 图片选择器 + val imagePickerLauncher = rememberLauncherForActivityResult( + contract = ActivityResultContracts.GetContent() + ) { uri -> + uri?.let { + // 将URI转换为File并上传 + val inputStream = context.contentResolver.openInputStream(uri) + val tempFile = File(context.cacheDir, "temp_avatar.jpg") + inputStream?.use { input -> + tempFile.outputStream().use { output -> + input.copyTo(output) + } + } + authViewModel.uploadAvatar(tempFile) + } + } // 初始化TokenManager LaunchedEffect(Unit) { @@ -97,42 +119,22 @@ fun ProfileScreen( AppSettings.speechRate = speechRate } - // 语速选择对话框 + // 语速选择对话框 - 显示开发中提示 if (showSpeechRateDialog) { AlertDialog( onDismissRequest = { showSpeechRateDialog = false }, - title = { Text("选择语速") }, - text = { - Column( + title = { Text("提示") }, + text = { + Text( + text = "语音模块正在开发中,敬请期待!", modifier = Modifier.wrapContentHeight() - ) { - AppSettings.SpeechRate.entries.forEach { rate -> - Row( - modifier = Modifier - .fillMaxWidth() - .clickable { - speechRate = rate - showSpeechRateDialog = false - } - .padding(vertical = 8.dp), - verticalAlignment = Alignment.CenterVertically - ) { - RadioButton( - selected = speechRate == rate, - onClick = { - speechRate = rate - showSpeechRateDialog = false - } - ) - Spacer(modifier = Modifier.width(8.dp)) - Text(rate.label) - } - } - } + ) }, confirmButton = { - TextButton(onClick = { showSpeechRateDialog = false }) { - Text("取消") + TextButton( + onClick = { showSpeechRateDialog = false } + ) { + Text("确定", color = LightPurple) } } ) @@ -153,14 +155,7 @@ fun ProfileScreen( horizontalAlignment = Alignment.CenterHorizontally ) { if (isLoggedIn && currentUser != null) { - // 已登录:显示用户信息 - // 使用新的UserAvatar组件 - com.huaga.life_echo.ui.components.profile.UserAvatar( - avatarUrl = userProfile?.avatarUrl ?: currentUser!!.avatar_url, - modifier = Modifier.size(80.dp) - ) - - Spacer(modifier = Modifier.height(16.dp)) + // 已登录:显示用户信息(暂时不显示头像) Text( text = userProfile?.nickname ?: currentUser!!.nickname, @@ -169,7 +164,26 @@ fun ProfileScreen( color = MaterialTheme.colorScheme.onSurface ) - Spacer(modifier = Modifier.height(4.dp)) + Spacer(modifier = Modifier.height(8.dp)) + + // 显示邮箱 + if (!currentUser!!.email.isNullOrBlank()) { + Text( + text = currentUser!!.email ?: "", + fontSize = 14.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + Spacer(modifier = Modifier.height(4.dp)) + } + + // 显示手机号 + Text( + text = currentUser!!.phone, + fontSize = 14.sp, + color = MaterialTheme.colorScheme.onSurfaceVariant + ) + + Spacer(modifier = Modifier.height(8.dp)) // 使用新的PlanStatusBadge组件 com.huaga.life_echo.ui.components.profile.PlanStatusBadge( @@ -270,9 +284,9 @@ fun ProfileScreen( SettingItem( icon = AppIcons.Upgrade, title = "升级/管理套餐", - subtitle = "解锁完整导出与更多功能", + subtitle = "功能开发中", onClick = { - navController?.navigate(com.huaga.life_echo.navigation.Screen.UpgradePlan.route) + showUpgradePlanDialog = true } ) HorizontalDivider(modifier = Modifier.padding(horizontal = 16.dp)) @@ -329,30 +343,15 @@ fun ProfileScreen( SettingItem( icon = AppIcons.AccessTime, title = "语速", - subtitle = speechRate.label, + subtitle = "正在开发语音模块", onClick = { - // 显示语速选择对话框 + // 显示提示对话框 showSpeechRateDialog = true } ) - HorizontalDivider(modifier = Modifier.padding(horizontal = 16.dp)) - SettingItem( - icon = AppIcons.FormatSize, - title = "大字模式", - trailing = { - Switch( - checked = largeFontMode, - onCheckedChange = { largeFontMode = it }, - colors = SwitchDefaults.colors( - checkedThumbColor = Color.White, - checkedTrackColor = LightPurple, - uncheckedThumbColor = Color.White, - uncheckedTrackColor = Color(0xFFE0E0E0) - ) - ) - }, - onClick = { largeFontMode = !largeFontMode } - ) + // 大字模式暂时不开放 + // HorizontalDivider(modifier = Modifier.padding(horizontal = 16.dp)) + // SettingItem(...) HorizontalDivider(modifier = Modifier.padding(horizontal = 16.dp)) SettingItem( icon = AppIcons.Brightness2, @@ -397,6 +396,27 @@ fun ProfileScreen( } } + // 升级套餐提示对话框 + if (showUpgradePlanDialog) { + AlertDialog( + onDismissRequest = { showUpgradePlanDialog = false }, + title = { Text("功能开发中") }, + text = { + Text( + text = "升级/管理套餐功能正在开发中,敬请期待!", + modifier = Modifier.wrapContentHeight() + ) + }, + confirmButton = { + TextButton( + onClick = { showUpgradePlanDialog = false } + ) { + Text("确定", color = LightPurple) + } + } + ) + } + // 登出确认对话框 if (showLogoutDialog) { AlertDialog(