refactor: 优化前端UI组件

- 优化ConversationListItem对话列表项组件
- 优化ChapterCard章节卡片组件
- 优化ChapterReadingView章节阅读视图
- 优化FullTextReadingView全文阅读视图
This commit is contained in:
iammm0
2026-01-29 10:57:13 +08:00
parent 2b9f79070d
commit 5508d94e54
4 changed files with 124 additions and 49 deletions

View File

@@ -1,15 +1,18 @@
package com.huaga.life_echo.ui.components.conversation package com.huaga.life_echo.ui.components.conversation
import androidx.compose.foundation.background 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.layout.*
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.material3.Icon import androidx.compose.material3.*
import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable 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.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
@@ -26,21 +29,52 @@ import com.huaga.life_echo.utils.TextUtils
/** /**
* 对话列表项组件 * 对话列表项组件
*/ */
@OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun ConversationListItem( fun ConversationListItem(
conversation: ConversationListItemDto, conversation: ConversationListItemDto,
onClick: () -> Unit, onClick: () -> Unit,
onDelete: (() -> Unit)? = null, onDelete: (() -> Unit)? = null,
isSelected: Boolean = false,
isSelectionMode: Boolean = false,
onLongClick: (() -> Unit)? = null,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
var showMenu by remember { mutableStateOf(false) }
Row( Row(
modifier = modifier modifier = modifier
.fillMaxWidth() .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), .padding(horizontal = 16.dp, vertical = 12.dp),
verticalAlignment = Alignment.CenterVertically 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( Column(
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
) { ) {
@@ -52,11 +86,13 @@ fun ConversationListItem(
fontSize = 16.sp, fontSize = 16.sp,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
color = MaterialTheme.colorScheme.onSurface, color = MaterialTheme.colorScheme.onSurface,
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f),
maxLines = 1
) )
// 未读消息数 // 未读消息数
if (conversation.unreadCount > 0) { if (conversation.unreadCount > 0) {
Spacer(modifier = Modifier.width(4.dp))
Box( Box(
modifier = Modifier modifier = Modifier
.size(18.dp) .size(18.dp)
@@ -77,10 +113,10 @@ fun ConversationListItem(
Spacer(modifier = Modifier.height(4.dp)) Spacer(modifier = Modifier.height(4.dp))
Text( Text(
text = TextUtils.ellipsizeSingleLine(conversation.latestMessagePreview, 50), text = TextUtils.ellipsizeSingleLine(conversation.latestMessagePreview, 100),
fontSize = 14.sp, fontSize = 14.sp,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
maxLines = 1 maxLines = 2
) )
} }
@@ -92,19 +128,41 @@ fun ConversationListItem(
modifier = Modifier.padding(start = 8.dp) modifier = Modifier.padding(start = 8.dp)
) )
// 删除按钮 // 三个点菜单按钮(仅在非多选模式时显示)
if (onDelete != null) { if (onDelete != null && !isSelectionMode) {
Spacer(modifier = Modifier.width(8.dp)) Spacer(modifier = Modifier.width(4.dp))
IconButton( Box {
onClick = { onDelete() }, IconButton(
modifier = Modifier.size(40.dp) onClick = { showMenu = true },
) { modifier = Modifier.size(40.dp)
Icon( ) {
imageVector = AppIcons.Delete, Icon(
contentDescription = "删除", imageVector = AppIcons.MoreVert,
tint = MaterialTheme.colorScheme.error, contentDescription = "更多选项",
modifier = Modifier.size(20.dp) 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( Box(
modifier = modifier modifier = modifier
.clip(RoundedCornerShape(8.dp)) .clip(RoundedCornerShape(8.dp))
.background(LightPurple), .background(if (isDefaultAssistant) LightPurple else MaterialTheme.colorScheme.primaryContainer),
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
if (avatarUrl != null) { if (avatarUrl != null && !isDefaultAssistant) {
// TODO: 使用Coil或Glide加载网络图片 // TODO: 使用Coil或Glide加载网络图片
// 暂时显示图标 // 暂时显示图标
Icon( Icon(
imageVector = AppIcons.Conversation, imageVector = AppIcons.Conversation,
contentDescription = "头像", contentDescription = "头像",
tint = Color.White, 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 { } else {
Icon( Icon(
imageVector = if (isDefaultAssistant) AppIcons.Book else AppIcons.Conversation, imageVector = AppIcons.Conversation,
contentDescription = "头像", contentDescription = "头像",
tint = Color.White, tint = Color.White,
modifier = Modifier.size(24.dp) modifier = Modifier.size(32.dp)
) )
} }
} }

View File

@@ -9,7 +9,6 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
import androidx.compose.material3.Icon
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.* 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.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import com.huaga.life_echo.network.models.ChapterDto 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.ui.theme.LightPurple
import com.huaga.life_echo.utils.TextUtils
/** /**
* 章节卡片组件(可展开显示详细内容) * 章节卡片组件(可展开显示详细内容)
@@ -72,8 +72,8 @@ fun ChapterCard(
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
) { ) {
Text( Text(
text = String.format("%02d", chapter.order_index), text = "${chapter.order_index}",
fontSize = 18.sp, fontSize = 14.sp,
fontWeight = FontWeight.Bold, fontWeight = FontWeight.Bold,
color = Color.White color = Color.White
) )
@@ -98,14 +98,6 @@ fun ChapterCard(
color = MaterialTheme.colorScheme.onSurfaceVariant 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() .fillMaxWidth()
.padding(16.dp) .padding(16.dp)
) { ) {
Text( // 使用Markdown渲染并移除{{IMAGE}}占位符(如果没有图片)
text = chapter.content, val processedContent = TextUtils.removeImagePlaceholders(
fontSize = 14.sp, chapter.content,
color = MaterialTheme.colorScheme.onSurface, hasImages = chapter.images.isNotEmpty()
lineHeight = 22.sp )
MarkdownText(
content = processedContent,
modifier = Modifier.fillMaxWidth(),
textColor = MaterialTheme.colorScheme.onSurface,
fontSize = 14,
lineHeight = 22
) )
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(12.dp))
// 查看详情按钮 // 查看详情按钮

View File

@@ -15,6 +15,7 @@ import androidx.compose.ui.unit.sp
import com.huaga.life_echo.network.models.ChapterContentDto import com.huaga.life_echo.network.models.ChapterContentDto
import com.huaga.life_echo.ui.components.common.MarkdownText import com.huaga.life_echo.ui.components.common.MarkdownText
import com.huaga.life_echo.ui.theme.LightPurple 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) modifier = Modifier.padding(bottom = 24.dp)
) )
// 正文内容(支持 Markdown // 正文内容(支持 Markdown,移除{{IMAGE}}占位符如果没有图片
val processedContent = TextUtils.removeImagePlaceholders(
chapter.content,
hasImages = chapter.images.isNotEmpty()
)
MarkdownText( MarkdownText(
content = chapter.content, content = processedContent,
modifier = Modifier.padding(bottom = 16.dp), modifier = Modifier.padding(bottom = 16.dp),
textColor = MaterialTheme.colorScheme.onSurface, textColor = MaterialTheme.colorScheme.onSurface,
fontSize = 16, fontSize = 16,

View File

@@ -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.components.common.MarkdownText
import com.huaga.life_echo.ui.icons.AppIcons import com.huaga.life_echo.ui.icons.AppIcons
import com.huaga.life_echo.ui.theme.LightPurple 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) modifier = Modifier.padding(bottom = 16.dp)
) )
// 章节内容(支持 Markdown // 章节内容(支持 Markdown,移除{{IMAGE}}占位符如果没有图片
val processedContent = TextUtils.removeImagePlaceholders(
chapter.content,
hasImages = chapter.images.isNotEmpty()
)
MarkdownText( MarkdownText(
content = chapter.content, content = processedContent,
modifier = Modifier.padding(bottom = 16.dp), modifier = Modifier.padding(bottom = 16.dp),
textColor = MaterialTheme.colorScheme.onSurface, textColor = MaterialTheme.colorScheme.onSurface,
fontSize = 16, fontSize = 16,