diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/components/conversation/ConversationListItem.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/components/conversation/ConversationListItem.kt index 10a1376..4538fbe 100644 --- a/app-android/app/src/main/java/com/huaga/life_echo/ui/components/conversation/ConversationListItem.kt +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/components/conversation/ConversationListItem.kt @@ -1,15 +1,18 @@ package com.huaga.life_echo.ui.components.conversation import androidx.compose.foundation.background -import androidx.compose.foundation.clickable +import androidx.compose.foundation.combinedClickable +import androidx.compose.foundation.ExperimentalFoundationApi +import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.CircleShape import androidx.compose.foundation.shape.RoundedCornerShape -import androidx.compose.material3.Icon -import androidx.compose.material3.IconButton -import androidx.compose.material3.MaterialTheme -import androidx.compose.material3.Text +import androidx.compose.material3.* 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 import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip @@ -26,21 +29,52 @@ import com.huaga.life_echo.utils.TextUtils /** * 对话列表项组件 */ +@OptIn(ExperimentalFoundationApi::class) @Composable fun ConversationListItem( conversation: ConversationListItemDto, onClick: () -> Unit, onDelete: (() -> Unit)? = null, + isSelected: Boolean = false, + isSelectionMode: Boolean = false, + onLongClick: (() -> Unit)? = null, modifier: Modifier = Modifier ) { + var showMenu by remember { mutableStateOf(false) } + Row( modifier = modifier .fillMaxWidth() - .clickable { onClick() } + .combinedClickable( + onClick = onClick, + onLongClick = onLongClick ?: {} + ) + .background( + if (isSelected) LightPurple.copy(alpha = 0.1f) + else Color.Transparent + ) .padding(horizontal = 16.dp, vertical = 12.dp), verticalAlignment = Alignment.CenterVertically ) { - // 对话信息(暂时不显示头像) + // 选择框(仅在多选模式时显示) + if (isSelectionMode) { + Checkbox( + checked = isSelected, + onCheckedChange = { onClick() }, + modifier = Modifier.padding(end = 12.dp) + ) + } + + // 头像 + ConversationAvatar( + avatarUrl = conversation.avatarUrl, + isDefaultAssistant = conversation.isDefaultAssistant, + modifier = Modifier.size(48.dp) + ) + + Spacer(modifier = Modifier.width(12.dp)) + + // 对话信息 Column( modifier = Modifier.weight(1f) ) { @@ -52,11 +86,13 @@ fun ConversationListItem( fontSize = 16.sp, fontWeight = FontWeight.Bold, color = MaterialTheme.colorScheme.onSurface, - modifier = Modifier.weight(1f) + modifier = Modifier.weight(1f), + maxLines = 1 ) // 未读消息数 if (conversation.unreadCount > 0) { + Spacer(modifier = Modifier.width(4.dp)) Box( modifier = Modifier .size(18.dp) @@ -77,10 +113,10 @@ fun ConversationListItem( Spacer(modifier = Modifier.height(4.dp)) Text( - text = TextUtils.ellipsizeSingleLine(conversation.latestMessagePreview, 50), + text = TextUtils.ellipsizeSingleLine(conversation.latestMessagePreview, 100), fontSize = 14.sp, color = MaterialTheme.colorScheme.onSurfaceVariant, - maxLines = 1 + maxLines = 2 ) } @@ -92,19 +128,41 @@ fun ConversationListItem( modifier = Modifier.padding(start = 8.dp) ) - // 删除按钮 - if (onDelete != null) { - Spacer(modifier = Modifier.width(8.dp)) - IconButton( - onClick = { onDelete() }, - modifier = Modifier.size(40.dp) - ) { - Icon( - imageVector = AppIcons.Delete, - contentDescription = "删除", - tint = MaterialTheme.colorScheme.error, - modifier = Modifier.size(20.dp) - ) + // 三个点菜单按钮(仅在非多选模式时显示) + if (onDelete != null && !isSelectionMode) { + Spacer(modifier = Modifier.width(4.dp)) + Box { + IconButton( + onClick = { showMenu = true }, + modifier = Modifier.size(40.dp) + ) { + Icon( + imageVector = AppIcons.MoreVert, + contentDescription = "更多选项", + tint = MaterialTheme.colorScheme.onSurfaceVariant, + modifier = Modifier.size(20.dp) + ) + } + + DropdownMenu( + expanded = showMenu, + onDismissRequest = { showMenu = false } + ) { + DropdownMenuItem( + text = { Text("删除", color = MaterialTheme.colorScheme.error) }, + onClick = { + showMenu = false + onDelete() + }, + leadingIcon = { + Icon( + imageVector = AppIcons.Delete, + contentDescription = null, + tint = MaterialTheme.colorScheme.error + ) + } + ) + } } } } @@ -122,24 +180,32 @@ fun ConversationAvatar( Box( modifier = modifier .clip(RoundedCornerShape(8.dp)) - .background(LightPurple), + .background(if (isDefaultAssistant) LightPurple else MaterialTheme.colorScheme.primaryContainer), contentAlignment = Alignment.Center ) { - if (avatarUrl != null) { + if (avatarUrl != null && !isDefaultAssistant) { // TODO: 使用Coil或Glide加载网络图片 // 暂时显示图标 Icon( imageVector = AppIcons.Conversation, contentDescription = "头像", tint = Color.White, - modifier = Modifier.size(24.dp) + modifier = Modifier.size(32.dp) + ) + } else if (isDefaultAssistant) { + // 回忆录助手使用应用图标(使用Book图标作为占位符,实际应该加载应用图标) + Icon( + imageVector = AppIcons.Book, + contentDescription = "回忆录助手", + tint = Color.White, + modifier = Modifier.size(32.dp) ) } else { Icon( - imageVector = if (isDefaultAssistant) AppIcons.Book else AppIcons.Conversation, + imageVector = AppIcons.Conversation, contentDescription = "头像", tint = Color.White, - modifier = Modifier.size(24.dp) + modifier = Modifier.size(32.dp) ) } } diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/components/memoir/ChapterCard.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/components/memoir/ChapterCard.kt index 1784b32..2ecaa1a 100644 --- a/app-android/app/src/main/java/com/huaga/life_echo/ui/components/memoir/ChapterCard.kt +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/components/memoir/ChapterCard.kt @@ -9,7 +9,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.material3.Card import androidx.compose.material3.CardDefaults import androidx.compose.material3.HorizontalDivider -import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme import androidx.compose.material3.Text import androidx.compose.runtime.* @@ -22,8 +21,9 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.sp import com.huaga.life_echo.network.models.ChapterDto -import com.huaga.life_echo.ui.icons.AppIcons +import com.huaga.life_echo.ui.components.common.MarkdownText import com.huaga.life_echo.ui.theme.LightPurple +import com.huaga.life_echo.utils.TextUtils /** * 章节卡片组件(可展开显示详细内容) @@ -72,8 +72,8 @@ fun ChapterCard( contentAlignment = Alignment.Center ) { Text( - text = String.format("%02d", chapter.order_index), - fontSize = 18.sp, + text = "第${chapter.order_index}章", + fontSize = 14.sp, fontWeight = FontWeight.Bold, color = Color.White ) @@ -98,14 +98,6 @@ fun ChapterCard( color = MaterialTheme.colorScheme.onSurfaceVariant ) } - - // 展开/收起图标 - Icon( - imageVector = if (isExpanded) AppIcons.ExpandLess else AppIcons.ExpandMore, - contentDescription = if (isExpanded) "收起" else "展开", - tint = MaterialTheme.colorScheme.onSurfaceVariant, - modifier = Modifier.size(24.dp) - ) } // 展开时显示详细内容 @@ -118,11 +110,18 @@ fun ChapterCard( .fillMaxWidth() .padding(16.dp) ) { - Text( - text = chapter.content, - fontSize = 14.sp, - color = MaterialTheme.colorScheme.onSurface, - lineHeight = 22.sp + // 使用Markdown渲染,并移除{{IMAGE}}占位符(如果没有图片) + val processedContent = TextUtils.removeImagePlaceholders( + chapter.content, + hasImages = chapter.images.isNotEmpty() + ) + + MarkdownText( + content = processedContent, + modifier = Modifier.fillMaxWidth(), + textColor = MaterialTheme.colorScheme.onSurface, + fontSize = 14, + lineHeight = 22 ) Spacer(modifier = Modifier.height(12.dp)) // 查看详情按钮 diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/components/memoir/ChapterReadingView.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/components/memoir/ChapterReadingView.kt index 0efb4bb..e30f11f 100644 --- a/app-android/app/src/main/java/com/huaga/life_echo/ui/components/memoir/ChapterReadingView.kt +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/components/memoir/ChapterReadingView.kt @@ -15,6 +15,7 @@ import androidx.compose.ui.unit.sp import com.huaga.life_echo.network.models.ChapterContentDto import com.huaga.life_echo.ui.components.common.MarkdownText import com.huaga.life_echo.ui.theme.LightPurple +import com.huaga.life_echo.utils.TextUtils /** * 章节阅读视图组件 @@ -47,9 +48,13 @@ fun ChapterReadingView( modifier = Modifier.padding(bottom = 24.dp) ) - // 正文内容(支持 Markdown) + // 正文内容(支持 Markdown,移除{{IMAGE}}占位符如果没有图片) + val processedContent = TextUtils.removeImagePlaceholders( + chapter.content, + hasImages = chapter.images.isNotEmpty() + ) MarkdownText( - content = chapter.content, + content = processedContent, modifier = Modifier.padding(bottom = 16.dp), textColor = MaterialTheme.colorScheme.onSurface, fontSize = 16, diff --git a/app-android/app/src/main/java/com/huaga/life_echo/ui/components/memoir/FullTextReadingView.kt b/app-android/app/src/main/java/com/huaga/life_echo/ui/components/memoir/FullTextReadingView.kt index 6e3ce41..8100fd9 100644 --- a/app-android/app/src/main/java/com/huaga/life_echo/ui/components/memoir/FullTextReadingView.kt +++ b/app-android/app/src/main/java/com/huaga/life_echo/ui/components/memoir/FullTextReadingView.kt @@ -20,6 +20,7 @@ import com.huaga.life_echo.network.models.ChapterContentDto import com.huaga.life_echo.ui.components.common.MarkdownText import com.huaga.life_echo.ui.icons.AppIcons import com.huaga.life_echo.ui.theme.LightPurple +import com.huaga.life_echo.utils.TextUtils /** * 全文阅读视图组件 @@ -61,9 +62,13 @@ fun FullTextReadingView( modifier = Modifier.padding(bottom = 16.dp) ) - // 章节内容(支持 Markdown) + // 章节内容(支持 Markdown,移除{{IMAGE}}占位符如果没有图片) + val processedContent = TextUtils.removeImagePlaceholders( + chapter.content, + hasImages = chapter.images.isNotEmpty() + ) MarkdownText( - content = chapter.content, + content = processedContent, modifier = Modifier.padding(bottom = 16.dp), textColor = MaterialTheme.colorScheme.onSurface, fontSize = 16,