refactor: 优化前端聊天UI组件

- 优化MessageBubble消息气泡组件
- 优化MessageList消息列表组件
This commit is contained in:
iammm0
2026-01-26 11:54:11 +08:00
parent 06a616bc83
commit a18bb83c5e
2 changed files with 85 additions and 9 deletions

View File

@@ -8,6 +8,11 @@ import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
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.shadow import androidx.compose.ui.draw.shadow
@@ -38,14 +43,14 @@ fun UserMessageBubble(
.shadow(2.dp, RoundedCornerShape(12.dp)), .shadow(2.dp, RoundedCornerShape(12.dp)),
shape = RoundedCornerShape(12.dp), shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors( colors = CardDefaults.cardColors(
containerColor = LightPurple.copy(alpha = 0.2f) containerColor = LightPurple // 使用完整的紫色,不再使用透明度
) )
) { ) {
Text( Text(
text = text, text = text,
modifier = Modifier.padding(12.dp), modifier = Modifier.padding(12.dp),
fontSize = 14.sp, fontSize = 14.sp,
color = MaterialTheme.colorScheme.onSurface, color = Color.White, // 使用白色文字,确保在紫色背景上清晰可见
lineHeight = 20.sp lineHeight = 20.sp
) )
} }
@@ -87,3 +92,65 @@ fun AIMessageBubble(
} }
} }
} }
/**
* 流式AI消息气泡带光标闪烁效果
*/
@Composable
fun StreamingAIMessageBubble(
text: String,
modifier: Modifier = Modifier
) {
var showCursor by remember { mutableStateOf(true) }
// 光标闪烁动画
LaunchedEffect(Unit) {
while (true) {
showCursor = true
kotlinx.coroutines.delay(500)
showCursor = false
kotlinx.coroutines.delay(500)
}
}
Row(
modifier = modifier
.fillMaxWidth()
.padding(horizontal = 16.dp, vertical = 8.dp),
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.Start
) {
Card(
modifier = Modifier
.weight(1f)
.shadow(2.dp, RoundedCornerShape(12.dp)),
shape = RoundedCornerShape(12.dp),
colors = CardDefaults.cardColors(
containerColor = MaterialTheme.colorScheme.surface
)
) {
Row(
modifier = Modifier.padding(12.dp),
verticalAlignment = androidx.compose.ui.Alignment.CenterVertically
) {
Text(
text = text,
fontSize = 14.sp,
color = MaterialTheme.colorScheme.onSurface,
lineHeight = 20.sp
)
// 光标闪烁效果
if (showCursor) {
Text(
text = "",
fontSize = 14.sp,
color = MaterialTheme.colorScheme.primary,
fontWeight = FontWeight.Bold,
modifier = Modifier.padding(start = 2.dp)
)
} else {
Spacer(modifier = Modifier.width(8.dp))
}
}
}
}
}

View File

@@ -20,6 +20,7 @@ import androidx.compose.ui.unit.sp
import com.huaga.life_echo.network.models.MessageDto import com.huaga.life_echo.network.models.MessageDto
import com.huaga.life_echo.ui.theme.LightPurple import com.huaga.life_echo.ui.theme.LightPurple
import com.huaga.life_echo.utils.TimeUtils import com.huaga.life_echo.utils.TimeUtils
import kotlinx.coroutines.delay
/** /**
* 消息列表组件 * 消息列表组件
@@ -35,10 +36,18 @@ fun MessageList(
) { ) {
val listState = rememberLazyListState() val listState = rememberLazyListState()
// 自动滚动到底部 // 自动滚动到底部 - 改进:流式输出时实时滚动
LaunchedEffect(messages.size, isStreaming) { LaunchedEffect(messages.size, isStreaming, streamingText) {
if (messages.isNotEmpty() || isStreaming) { val targetIndex = messages.size + if (isStreaming) 1 else 0
listState.animateScrollToItem(messages.size) if (targetIndex > 0) {
// 流式输出时使用平滑滚动,其他情况使用动画滚动
if (isStreaming && streamingText.isNotEmpty()) {
// 流式输出时,每次文本更新都滚动到底部
kotlinx.coroutines.delay(50) // 短暂延迟确保内容已渲染
listState.animateScrollToItem(targetIndex)
} else {
listState.animateScrollToItem(targetIndex)
}
} }
} }
@@ -78,10 +87,10 @@ fun MessageList(
lastDate = currentDate lastDate = currentDate
} }
// 流式消息显示 // 流式消息显示 - 使用专门的流式消息气泡组件
if (isStreaming) { if (isStreaming) {
item { item(key = "streaming_message") {
AIMessageBubble( StreamingAIMessageBubble(
text = streamingText text = streamingText
) )
} }