refactor: 优化前端UI组件

- 优化MessageBubble消息气泡组件
- 优化ErrorView错误视图组件
- 优化WebSocketDebugPanel调试面板
- 优化ChapterCard章节卡片组件
- 优化ChapterReadingView章节阅读视图
- 优化FullTextReadingView全文阅读视图
- 优化OrganizeConversationDialog组织对话对话框
- 更新AppIcons图标
This commit is contained in:
iammm0
2026-01-28 12:59:32 +08:00
parent 4d5433d2e8
commit d5d8619f22
8 changed files with 104 additions and 26 deletions

View File

@@ -20,6 +20,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.text.font.FontWeight 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.ui.components.common.MarkdownText
import com.huaga.life_echo.ui.theme.LightPurple import com.huaga.life_echo.ui.theme.LightPurple
/** /**
@@ -59,6 +60,7 @@ fun UserMessageBubble(
/** /**
* AI消息气泡左侧/白色) * AI消息气泡左侧/白色)
* 支持 Markdown 格式渲染
*/ */
@Composable @Composable
fun AIMessageBubble( fun AIMessageBubble(
@@ -82,12 +84,12 @@ fun AIMessageBubble(
containerColor = MaterialTheme.colorScheme.surface containerColor = MaterialTheme.colorScheme.surface
) )
) { ) {
Text( MarkdownText(
text = text, content = text,
modifier = Modifier.padding(12.dp), modifier = Modifier.padding(12.dp),
fontSize = 14.sp, textColor = MaterialTheme.colorScheme.onSurface,
color = MaterialTheme.colorScheme.onSurface, fontSize = 14,
lineHeight = 20.sp lineHeight = 20
) )
} }
} }

View File

@@ -11,17 +11,44 @@ import androidx.compose.ui.Modifier
import androidx.compose.ui.text.style.TextAlign import androidx.compose.ui.text.style.TextAlign
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.config.AppConfig
import com.huaga.life_echo.ui.theme.LightPurple import com.huaga.life_echo.ui.theme.LightPurple
/** /**
* 错误视图组件 * 错误视图组件
*
* 生产模式下自动转换技术性错误为友好提示
* 开发模式下显示原始错误信息便于调试
*
* @param message 错误信息(可以是原始错误或自定义信息)
* @param errorType 可选的错误类型,用于显示更精确的友好提示
* @param onRetry 重试回调
*/ */
@Composable @Composable
fun ErrorView( fun ErrorView(
message: String, message: String,
errorType: ErrorType? = null,
onRetry: (() -> Unit)? = null, onRetry: (() -> Unit)? = null,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
// 如果指定了错误类型,使用友好错误视图
if (errorType != null) {
FriendlyErrorView(
errorType = errorType,
originalMessage = message,
onRetry = onRetry,
modifier = modifier
)
return
}
// 处理显示消息:生产模式下过滤技术性错误
val displayMessage = if (AppConfig.isDebugMode) {
message
} else {
sanitizeErrorMessage(message)
}
Column( Column(
modifier = modifier modifier = modifier
.fillMaxWidth() .fillMaxWidth()
@@ -35,7 +62,7 @@ fun ErrorView(
) )
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(16.dp))
Text( Text(
text = message, text = displayMessage,
fontSize = 16.sp, fontSize = 16.sp,
color = MaterialTheme.colorScheme.onSurfaceVariant, color = MaterialTheme.colorScheme.onSurfaceVariant,
textAlign = TextAlign.Center textAlign = TextAlign.Center
@@ -53,3 +80,40 @@ fun ErrorView(
} }
} }
} }
/**
* 清理错误消息,将技术性错误转换为用户友好的提示
*/
private fun sanitizeErrorMessage(message: String): String {
val lowerMessage = message.lowercase()
return when {
// 网络相关错误
lowerMessage.contains("timeout") || lowerMessage.contains("timed out") ->
"网络响应超时,请稍后重试"
lowerMessage.contains("connect") || lowerMessage.contains("network") ||
lowerMessage.contains("socket") || lowerMessage.contains("unreachable") ->
"网络连接失败,请检查网络后重试"
// 服务器相关错误
lowerMessage.contains("500") || lowerMessage.contains("502") ||
lowerMessage.contains("503") || lowerMessage.contains("server error") ->
"服务暂时不可用,请稍后重试"
// 认证相关错误
lowerMessage.contains("401") || lowerMessage.contains("unauthorized") ||
lowerMessage.contains("token") && lowerMessage.contains("expired") ->
"登录已过期,请重新登录"
// 资源不存在
lowerMessage.contains("404") || lowerMessage.contains("not found") ->
"内容不存在或已被删除"
// 异常和技术性错误(包含堆栈信息的)
lowerMessage.contains("exception") || lowerMessage.contains("stacktrace") ||
message.contains("\n") || message.length > 100 ->
"操作失败,请稍后重试"
// 保留业务相关的简短错误信息
else -> message
}
}

View File

@@ -114,7 +114,7 @@ fun WebSocketDebugPanel(
DebugInfoRow("是否已连接", if (isConnected) "" else "", if (isConnected) Color(0xFF4CAF50) else Color(0xFFF44336)) DebugInfoRow("是否已连接", if (isConnected) "" else "", if (isConnected) Color(0xFF4CAF50) else Color(0xFFF44336))
DebugInfoRow("对话ID", conversationId ?: "未设置", MaterialTheme.colorScheme.onSurfaceVariant) DebugInfoRow("对话ID", conversationId ?: "未设置", MaterialTheme.colorScheme.onSurfaceVariant)
Divider(modifier = Modifier.padding(vertical = 4.dp)) HorizontalDivider(modifier = Modifier.padding(vertical = 4.dp))
// 消息统计 // 消息统计
DebugInfoRow("消息总数", "$messageCount", MaterialTheme.colorScheme.primary) DebugInfoRow("消息总数", "$messageCount", MaterialTheme.colorScheme.primary)
@@ -123,7 +123,7 @@ fun WebSocketDebugPanel(
// 最后一条消息信息 // 最后一条消息信息
if (lastMessageType != null) { if (lastMessageType != null) {
Divider(modifier = Modifier.padding(vertical = 4.dp)) HorizontalDivider(modifier = Modifier.padding(vertical = 4.dp))
DebugInfoRow("最后消息类型", lastMessageType, MaterialTheme.colorScheme.primary) DebugInfoRow("最后消息类型", lastMessageType, MaterialTheme.colorScheme.primary)
if (lastMessageTime != null) { if (lastMessageTime != null) {
DebugInfoRow("最后消息时间", lastMessageTime, MaterialTheme.colorScheme.onSurfaceVariant) DebugInfoRow("最后消息时间", lastMessageTime, MaterialTheme.colorScheme.onSurfaceVariant)
@@ -132,7 +132,7 @@ fun WebSocketDebugPanel(
// 错误信息 // 错误信息
if (errorMessages.isNotEmpty()) { if (errorMessages.isNotEmpty()) {
Divider(modifier = Modifier.padding(vertical = 4.dp)) HorizontalDivider(modifier = Modifier.padding(vertical = 4.dp))
Text( Text(
text = "错误日志", text = "错误日志",
fontSize = 12.sp, fontSize = 12.sp,

View File

@@ -8,6 +8,7 @@ import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape 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.Icon 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
@@ -109,7 +110,7 @@ fun ChapterCard(
// 展开时显示详细内容 // 展开时显示详细内容
if (isExpanded) { if (isExpanded) {
androidx.compose.material3.Divider( HorizontalDivider(
modifier = Modifier.padding(horizontal = 16.dp) modifier = Modifier.padding(horizontal = 16.dp)
) )
Column( Column(

View File

@@ -13,10 +13,12 @@ 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.ChapterContentDto 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.ui.theme.LightPurple
/** /**
* 章节阅读视图组件 * 章节阅读视图组件
* 支持 Markdown 格式渲染
*/ */
@Composable @Composable
fun ChapterReadingView( fun ChapterReadingView(
@@ -45,13 +47,13 @@ fun ChapterReadingView(
modifier = Modifier.padding(bottom = 24.dp) modifier = Modifier.padding(bottom = 24.dp)
) )
// 正文内容 // 正文内容(支持 Markdown
Text( MarkdownText(
text = chapter.content, content = chapter.content,
fontSize = 16.sp, modifier = Modifier.padding(bottom = 16.dp),
lineHeight = 28.sp, textColor = MaterialTheme.colorScheme.onSurface,
color = MaterialTheme.colorScheme.onSurface, fontSize = 16,
modifier = Modifier.padding(bottom = 16.dp) lineHeight = 28
) )
// 引用块 // 引用块

View File

@@ -17,11 +17,13 @@ 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.ChapterContentDto 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.icons.AppIcons
import com.huaga.life_echo.ui.theme.LightPurple import com.huaga.life_echo.ui.theme.LightPurple
/** /**
* 全文阅读视图组件 * 全文阅读视图组件
* 支持 Markdown 格式渲染
*/ */
@Composable @Composable
fun FullTextReadingView( fun FullTextReadingView(
@@ -59,13 +61,13 @@ fun FullTextReadingView(
modifier = Modifier.padding(bottom = 16.dp) modifier = Modifier.padding(bottom = 16.dp)
) )
// 章节内容 // 章节内容(支持 Markdown
Text( MarkdownText(
text = chapter.content, content = chapter.content,
fontSize = 16.sp, modifier = Modifier.padding(bottom = 16.dp),
lineHeight = 28.sp, textColor = MaterialTheme.colorScheme.onSurface,
color = MaterialTheme.colorScheme.onSurface, fontSize = 16,
modifier = Modifier.padding(bottom = 16.dp) lineHeight = 28
) )
// 引用块 // 引用块

View File

@@ -62,7 +62,7 @@ fun OrganizeConversationDialog(
} }
} }
Divider() HorizontalDivider()
// 对话列表 // 对话列表
if (isLoading) { if (isLoading) {

View File

@@ -3,6 +3,7 @@ package com.huaga.life_echo.ui.icons
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.icons.automirrored.filled.Chat import androidx.compose.material.icons.automirrored.filled.Chat
import androidx.compose.material.icons.automirrored.filled.ExitToApp
import androidx.compose.material.icons.automirrored.filled.Help import androidx.compose.material.icons.automirrored.filled.Help
import androidx.compose.material.icons.automirrored.filled.MenuBook import androidx.compose.material.icons.automirrored.filled.MenuBook
import androidx.compose.material.icons.automirrored.filled.Send import androidx.compose.material.icons.automirrored.filled.Send
@@ -85,6 +86,12 @@ object AppIcons {
val ManageAccounts = Icons.Default.ManageAccounts val ManageAccounts = Icons.Default.ManageAccounts
val Lock = Icons.Default.Lock val Lock = Icons.Default.Lock
val Phone = Icons.Default.Phone val Phone = Icons.Default.Phone
val ExitToApp = Icons.Default.ExitToApp val ExitToApp = Icons.AutoMirrored.Filled.ExitToApp
val DevicesOther = Icons.Default.DevicesOther val DevicesOther = Icons.Default.DevicesOther
// 错误处理图标
val WifiOff = Icons.Default.WifiOff
val CloudOff = Icons.Default.CloudOff
val SearchOff = Icons.Default.SearchOff
val SignalWifiOff = Icons.Default.SignalWifiOff
} }