refactor: 优化前端聊天UI组件
- 优化MessageBubble消息气泡组件 - 优化MessageList消息列表组件
This commit is contained in:
@@ -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))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user