refactor: 优化前端UI组件
- 优化MessageBubble消息气泡组件 - 优化ErrorView错误视图组件 - 优化WebSocketDebugPanel调试面板 - 优化ChapterCard章节卡片组件 - 优化ChapterReadingView章节阅读视图 - 优化FullTextReadingView全文阅读视图 - 优化OrganizeConversationDialog组织对话对话框 - 更新AppIcons图标
This commit is contained in:
@@ -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
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
// 引用块
|
// 引用块
|
||||||
|
|||||||
@@ -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
|
||||||
)
|
)
|
||||||
|
|
||||||
// 引用块
|
// 引用块
|
||||||
|
|||||||
@@ -62,7 +62,7 @@ fun OrganizeConversationDialog(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Divider()
|
HorizontalDivider()
|
||||||
|
|
||||||
// 对话列表
|
// 对话列表
|
||||||
if (isLoading) {
|
if (isLoading) {
|
||||||
|
|||||||
@@ -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
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user