refactor: 优化前端UI组件
- 优化聊天相关组件(MessageBubble、MessageList、TypingIndicator) - 优化对话相关组件(ConversationListHeader、ConversationListItem) - 优化回忆录相关组件(BookInfoCard、ChapterCard) - 优化通用组件(EmptyStateView、UserAvatar)
This commit is contained in:
@@ -67,16 +67,7 @@ fun AIMessageBubble(
|
|||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||||
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.Start
|
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.Start
|
||||||
) {
|
) {
|
||||||
// 头像
|
// 消息气泡(暂时不显示头像)
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.size(32.dp)
|
|
||||||
.padding(end = 8.dp)
|
|
||||||
) {
|
|
||||||
avatar()
|
|
||||||
}
|
|
||||||
|
|
||||||
// 消息气泡
|
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.weight(1f)
|
.weight(1f)
|
||||||
|
|||||||
@@ -69,19 +69,7 @@ fun MessageList(
|
|||||||
}
|
}
|
||||||
"assistant" -> {
|
"assistant" -> {
|
||||||
AIMessageBubble(
|
AIMessageBubble(
|
||||||
text = message.content,
|
text = message.content
|
||||||
avatar = {
|
|
||||||
// AI头像
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.size(32.dp)
|
|
||||||
.clip(RoundedCornerShape(8.dp))
|
|
||||||
.background(LightPurple),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
Text("📖", fontSize = 20.sp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -94,24 +82,15 @@ fun MessageList(
|
|||||||
if (isStreaming) {
|
if (isStreaming) {
|
||||||
item {
|
item {
|
||||||
AIMessageBubble(
|
AIMessageBubble(
|
||||||
text = streamingText,
|
text = streamingText
|
||||||
avatar = {
|
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.size(32.dp)
|
|
||||||
.clip(RoundedCornerShape(8.dp))
|
|
||||||
.background(LightPurple),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
Text("📖", fontSize = 20.sp)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// 正在输入指示器
|
// 正在输入指示器 - 显示加载动画
|
||||||
if ((isStreaming && streamingText.isEmpty()) || (isTyping && !isStreaming)) {
|
// 1. 如果正在流式接收但还没有内容,显示加载动画
|
||||||
|
// 2. 如果设置了isTyping且不在流式状态,显示加载动画
|
||||||
|
if (isTyping || (isStreaming && streamingText.isEmpty())) {
|
||||||
item {
|
item {
|
||||||
TypingIndicator()
|
TypingIndicator()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -17,6 +17,7 @@ import androidx.compose.ui.draw.clip
|
|||||||
import androidx.compose.ui.draw.shadow
|
import androidx.compose.ui.draw.shadow
|
||||||
import androidx.compose.ui.graphics.Color
|
import androidx.compose.ui.graphics.Color
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import androidx.compose.ui.unit.sp
|
||||||
import com.huaga.life_echo.ui.theme.LightPurple
|
import com.huaga.life_echo.ui.theme.LightPurple
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -32,20 +33,7 @@ fun TypingIndicator(
|
|||||||
.padding(horizontal = 16.dp, vertical = 8.dp),
|
.padding(horizontal = 16.dp, vertical = 8.dp),
|
||||||
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.Start
|
horizontalArrangement = androidx.compose.foundation.layout.Arrangement.Start
|
||||||
) {
|
) {
|
||||||
// 头像占位
|
// 消息气泡(暂时不显示头像)
|
||||||
Box(
|
|
||||||
modifier = Modifier
|
|
||||||
.size(32.dp)
|
|
||||||
.clip(RoundedCornerShape(8.dp))
|
|
||||||
.background(LightPurple),
|
|
||||||
contentAlignment = Alignment.Center
|
|
||||||
) {
|
|
||||||
// 可以显示一个图标或占位符
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
|
||||||
|
|
||||||
// 消息气泡
|
|
||||||
Card(
|
Card(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.shadow(2.dp, RoundedCornerShape(12.dp)),
|
.shadow(2.dp, RoundedCornerShape(12.dp)),
|
||||||
@@ -116,6 +104,6 @@ private fun Dot(alpha: Float) {
|
|||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.size(8.dp)
|
.size(8.dp)
|
||||||
.clip(CircleShape)
|
.clip(CircleShape)
|
||||||
.background(Color.Gray.copy(alpha = alpha))
|
.background(LightPurple.copy(alpha = alpha))
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ import androidx.compose.ui.unit.sp
|
|||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun EmptyStateView(
|
fun EmptyStateView(
|
||||||
|
title: String? = null,
|
||||||
message: String,
|
message: String,
|
||||||
icon: String = "📭",
|
icon: String = "📭",
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
@@ -31,6 +32,16 @@ fun EmptyStateView(
|
|||||||
fontSize = 48.sp
|
fontSize = 48.sp
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(16.dp))
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
|
if (title != null) {
|
||||||
|
Text(
|
||||||
|
text = title,
|
||||||
|
fontSize = 20.sp,
|
||||||
|
fontWeight = androidx.compose.ui.text.font.FontWeight.Bold,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
textAlign = TextAlign.Center
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(8.dp))
|
||||||
|
}
|
||||||
Text(
|
Text(
|
||||||
text = message,
|
text = message,
|
||||||
fontSize = 16.sp,
|
fontSize = 16.sp,
|
||||||
|
|||||||
@@ -1,7 +1,11 @@
|
|||||||
package com.huaga.life_echo.ui.components.conversation
|
package com.huaga.life_echo.ui.components.conversation
|
||||||
|
|
||||||
|
import android.annotation.SuppressLint
|
||||||
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
|
import androidx.compose.material3.Button
|
||||||
|
import androidx.compose.material3.ButtonDefaults
|
||||||
import androidx.compose.material3.Surface
|
import androidx.compose.material3.Surface
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.Composable
|
||||||
@@ -11,6 +15,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.icons.AppIcons
|
||||||
import com.huaga.life_echo.ui.theme.LightPurple
|
import com.huaga.life_echo.ui.theme.LightPurple
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -18,7 +23,8 @@ import com.huaga.life_echo.ui.theme.LightPurple
|
|||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun ConversationListHeader(
|
fun ConversationListHeader(
|
||||||
modifier: Modifier = Modifier
|
onCreateConversation: () -> Unit = {},
|
||||||
|
@SuppressLint("ModifierParameter") modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
Surface(
|
Surface(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = modifier.fillMaxWidth(),
|
||||||
@@ -31,18 +37,28 @@ fun ConversationListHeader(
|
|||||||
.padding(top = 16.dp, bottom = 24.dp, start = 16.dp, end = 16.dp),
|
.padding(top = 16.dp, bottom = 24.dp, start = 16.dp, end = 16.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
Text(
|
Spacer(modifier = Modifier.height(16.dp))
|
||||||
text = "往事拾遗",
|
// 新建对话按钮
|
||||||
fontSize = 24.sp,
|
Button(
|
||||||
fontWeight = FontWeight.Bold,
|
onClick = onCreateConversation,
|
||||||
color = Color.White
|
colors = ButtonDefaults.buttonColors(
|
||||||
|
containerColor = Color.White,
|
||||||
|
contentColor = LightPurple
|
||||||
|
),
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
androidx.compose.material3.Icon(
|
||||||
|
imageVector = AppIcons.Add,
|
||||||
|
contentDescription = "新建对话",
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
Text(
|
Text(
|
||||||
text = "用对话,留住珍贵的记忆",
|
text = "新建对话",
|
||||||
fontSize = 14.sp,
|
fontSize = 16.sp,
|
||||||
color = Color.White.copy(alpha = 0.9f)
|
fontWeight = FontWeight.Bold
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -6,6 +6,7 @@ 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.Icon
|
||||||
|
import androidx.compose.material3.IconButton
|
||||||
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
|
||||||
@@ -29,6 +30,7 @@ import com.huaga.life_echo.utils.TextUtils
|
|||||||
fun ConversationListItem(
|
fun ConversationListItem(
|
||||||
conversation: ConversationListItemDto,
|
conversation: ConversationListItemDto,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
|
onDelete: (() -> Unit)? = null,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
@@ -38,16 +40,7 @@ fun ConversationListItem(
|
|||||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
// 头像
|
// 对话信息(暂时不显示头像)
|
||||||
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)
|
||||||
) {
|
) {
|
||||||
@@ -98,6 +91,22 @@ fun ConversationListItem(
|
|||||||
timestamp = conversation.latestMessageTime,
|
timestamp = conversation.latestMessageTime,
|
||||||
modifier = Modifier.padding(start = 8.dp)
|
modifier = Modifier.padding(start = 8.dp)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
// 删除按钮
|
||||||
|
if (onDelete != null) {
|
||||||
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
|
IconButton(
|
||||||
|
onClick = { onDelete() },
|
||||||
|
modifier = Modifier.size(40.dp)
|
||||||
|
) {
|
||||||
|
Icon(
|
||||||
|
imageVector = AppIcons.Delete,
|
||||||
|
contentDescription = "删除",
|
||||||
|
tint = MaterialTheme.colorScheme.error,
|
||||||
|
modifier = Modifier.size(20.dp)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1,35 +1,21 @@
|
|||||||
package com.huaga.life_echo.ui.components.memoir
|
package com.huaga.life_echo.ui.components.memoir
|
||||||
|
|
||||||
import androidx.compose.foundation.background
|
|
||||||
import androidx.compose.foundation.clickable
|
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
|
||||||
import androidx.compose.material3.Card
|
import androidx.compose.material3.Card
|
||||||
import androidx.compose.material3.CardDefaults
|
import androidx.compose.material3.CardDefaults
|
||||||
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.material3.TextField
|
|
||||||
import androidx.compose.material3.TextFieldDefaults
|
|
||||||
import androidx.compose.runtime.*
|
import androidx.compose.runtime.*
|
||||||
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.graphics.Color
|
|
||||||
import androidx.compose.ui.platform.LocalSoftwareKeyboardController
|
|
||||||
import androidx.compose.ui.text.font.FontWeight
|
import androidx.compose.ui.text.font.FontWeight
|
||||||
import androidx.compose.ui.text.input.ImeAction
|
|
||||||
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.BookDto
|
import com.huaga.life_echo.network.models.BookDto
|
||||||
import com.huaga.life_echo.ui.icons.AppIcons
|
|
||||||
import com.huaga.life_echo.ui.theme.LightPurple
|
|
||||||
import com.huaga.life_echo.utils.TimeUtils
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 书籍信息卡片(支持书名编辑)
|
* 书籍信息卡片
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun BookInfoCard(
|
fun BookInfoCard(
|
||||||
@@ -38,15 +24,6 @@ fun BookInfoCard(
|
|||||||
onSubtitleChange: ((String?) -> Unit)? = null,
|
onSubtitleChange: ((String?) -> Unit)? = null,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
var isEditingTitle by remember { mutableStateOf(false) }
|
|
||||||
var editedTitle by remember { mutableStateOf(book.title) }
|
|
||||||
val keyboardController = LocalSoftwareKeyboardController.current
|
|
||||||
|
|
||||||
// 当book变化时更新编辑状态
|
|
||||||
LaunchedEffect(book.title) {
|
|
||||||
editedTitle = book.title
|
|
||||||
}
|
|
||||||
|
|
||||||
Card(
|
Card(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = modifier.fillMaxWidth(),
|
||||||
shape = RoundedCornerShape(12.dp),
|
shape = RoundedCornerShape(12.dp),
|
||||||
@@ -60,54 +37,14 @@ fun BookInfoCard(
|
|||||||
.padding(20.dp),
|
.padding(20.dp),
|
||||||
horizontalAlignment = Alignment.CenterHorizontally
|
horizontalAlignment = Alignment.CenterHorizontally
|
||||||
) {
|
) {
|
||||||
// 书名(可编辑)
|
// 书名(只显示,不可编辑)
|
||||||
if (isEditingTitle) {
|
|
||||||
TextField(
|
|
||||||
value = editedTitle,
|
|
||||||
onValueChange = { editedTitle = it },
|
|
||||||
singleLine = true,
|
|
||||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
|
||||||
keyboardActions = KeyboardActions(
|
|
||||||
onDone = {
|
|
||||||
isEditingTitle = false
|
|
||||||
keyboardController?.hide()
|
|
||||||
if (editedTitle.isNotBlank()) {
|
|
||||||
onTitleChange(editedTitle)
|
|
||||||
} else {
|
|
||||||
editedTitle = book.title
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
colors = TextFieldDefaults.colors(
|
|
||||||
focusedTextColor = MaterialTheme.colorScheme.primary,
|
|
||||||
unfocusedTextColor = MaterialTheme.colorScheme.primary
|
|
||||||
),
|
|
||||||
textStyle = MaterialTheme.typography.headlineMedium.copy(
|
|
||||||
fontWeight = FontWeight.Bold
|
|
||||||
)
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.clickable { isEditingTitle = true }
|
|
||||||
.padding(vertical = 4.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Text(
|
Text(
|
||||||
text = book.title,
|
text = book.title,
|
||||||
fontSize = 32.sp,
|
fontSize = 32.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
color = MaterialTheme.colorScheme.primary
|
color = MaterialTheme.colorScheme.primary,
|
||||||
|
modifier = Modifier.padding(vertical = 4.dp)
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
|
||||||
Icon(
|
|
||||||
imageVector = AppIcons.Edit,
|
|
||||||
contentDescription = "编辑",
|
|
||||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
modifier = Modifier.size(20.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
|
||||||
|
|||||||
@@ -1,5 +1,7 @@
|
|||||||
package com.huaga.life_echo.ui.components.memoir
|
package com.huaga.life_echo.ui.components.memoir
|
||||||
|
|
||||||
|
import androidx.compose.animation.animateContentSize
|
||||||
|
import androidx.compose.animation.core.tween
|
||||||
import androidx.compose.foundation.background
|
import androidx.compose.foundation.background
|
||||||
import androidx.compose.foundation.clickable
|
import androidx.compose.foundation.clickable
|
||||||
import androidx.compose.foundation.layout.*
|
import androidx.compose.foundation.layout.*
|
||||||
@@ -9,7 +11,7 @@ import androidx.compose.material3.CardDefaults
|
|||||||
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
|
||||||
import androidx.compose.runtime.Composable
|
import androidx.compose.runtime.*
|
||||||
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
|
||||||
@@ -23,7 +25,7 @@ import com.huaga.life_echo.ui.icons.AppIcons
|
|||||||
import com.huaga.life_echo.ui.theme.LightPurple
|
import com.huaga.life_echo.ui.theme.LightPurple
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 章节卡片组件(显示页数)
|
* 章节卡片组件(可展开显示详细内容)
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun ChapterCard(
|
fun ChapterCard(
|
||||||
@@ -31,6 +33,8 @@ fun ChapterCard(
|
|||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
|
var isExpanded by remember { mutableStateOf(false) }
|
||||||
|
|
||||||
val statusText = when (chapter.status) {
|
val statusText = when (chapter.status) {
|
||||||
"completed" -> "已整理"
|
"completed" -> "已整理"
|
||||||
"partial" -> "部分整理"
|
"partial" -> "部分整理"
|
||||||
@@ -40,16 +44,21 @@ fun ChapterCard(
|
|||||||
Card(
|
Card(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.clickable { onClick() }
|
.shadow(2.dp, RoundedCornerShape(12.dp))
|
||||||
.shadow(2.dp, RoundedCornerShape(12.dp)),
|
.animateContentSize(animationSpec = tween(300)),
|
||||||
shape = RoundedCornerShape(12.dp),
|
shape = RoundedCornerShape(12.dp),
|
||||||
colors = CardDefaults.cardColors(
|
colors = CardDefaults.cardColors(
|
||||||
containerColor = MaterialTheme.colorScheme.surface
|
containerColor = MaterialTheme.colorScheme.surface
|
||||||
)
|
)
|
||||||
) {
|
) {
|
||||||
|
Column(
|
||||||
|
modifier = Modifier.fillMaxWidth()
|
||||||
|
) {
|
||||||
|
// 章节头部(可点击展开/收起)
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
|
.clickable { isExpanded = !isExpanded }
|
||||||
.padding(16.dp),
|
.padding(16.dp),
|
||||||
verticalAlignment = Alignment.CenterVertically
|
verticalAlignment = Alignment.CenterVertically
|
||||||
) {
|
) {
|
||||||
@@ -89,13 +98,44 @@ fun ChapterCard(
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
// 右箭头
|
// 展开/收起图标
|
||||||
Icon(
|
Icon(
|
||||||
imageVector = AppIcons.ChevronRight,
|
imageVector = if (isExpanded) AppIcons.ExpandLess else AppIcons.ExpandMore,
|
||||||
contentDescription = "进入",
|
contentDescription = if (isExpanded) "收起" else "展开",
|
||||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
||||||
modifier = Modifier.size(24.dp)
|
modifier = Modifier.size(24.dp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// 展开时显示详细内容
|
||||||
|
if (isExpanded) {
|
||||||
|
androidx.compose.material3.Divider(
|
||||||
|
modifier = Modifier.padding(horizontal = 16.dp)
|
||||||
|
)
|
||||||
|
Column(
|
||||||
|
modifier = Modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.padding(16.dp)
|
||||||
|
) {
|
||||||
|
Text(
|
||||||
|
text = chapter.content,
|
||||||
|
fontSize = 14.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurface,
|
||||||
|
lineHeight = 22.sp
|
||||||
|
)
|
||||||
|
Spacer(modifier = Modifier.height(12.dp))
|
||||||
|
// 查看详情按钮
|
||||||
|
androidx.compose.material3.Button(
|
||||||
|
onClick = onClick,
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
colors = androidx.compose.material3.ButtonDefaults.buttonColors(
|
||||||
|
containerColor = LightPurple
|
||||||
|
)
|
||||||
|
) {
|
||||||
|
Text("查看详情", color = Color.White)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,12 +9,17 @@ import androidx.compose.runtime.Composable
|
|||||||
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
|
||||||
|
import androidx.compose.ui.layout.ContentScale
|
||||||
|
import androidx.compose.ui.platform.LocalContext
|
||||||
import androidx.compose.ui.unit.dp
|
import androidx.compose.ui.unit.dp
|
||||||
|
import coil.compose.AsyncImage
|
||||||
|
import coil.request.ImageRequest
|
||||||
|
import com.huaga.life_echo.config.AppConfig
|
||||||
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
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 用户头像组件(支持占位符)
|
* 用户头像组件(支持占位符和网络图片加载)
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun UserAvatar(
|
fun UserAvatar(
|
||||||
@@ -22,6 +27,8 @@ fun UserAvatar(
|
|||||||
modifier: Modifier = Modifier,
|
modifier: Modifier = Modifier,
|
||||||
size: androidx.compose.ui.unit.Dp = 80.dp
|
size: androidx.compose.ui.unit.Dp = 80.dp
|
||||||
) {
|
) {
|
||||||
|
val context = LocalContext.current
|
||||||
|
|
||||||
Box(
|
Box(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.size(size)
|
.size(size)
|
||||||
@@ -29,13 +36,24 @@ fun UserAvatar(
|
|||||||
.background(LightPurple.copy(alpha = 0.2f)),
|
.background(LightPurple.copy(alpha = 0.2f)),
|
||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
if (avatarUrl != null) {
|
if (!avatarUrl.isNullOrBlank()) {
|
||||||
// TODO: 使用Coil或Glide加载网络图片
|
// 构建完整的图片URL
|
||||||
Icon(
|
val fullUrl = if (avatarUrl.startsWith("http")) {
|
||||||
imageVector = AppIcons.Person,
|
avatarUrl
|
||||||
|
} else {
|
||||||
|
"${AppConfig.BASE_URL}$avatarUrl"
|
||||||
|
}
|
||||||
|
|
||||||
|
AsyncImage(
|
||||||
|
model = ImageRequest.Builder(context)
|
||||||
|
.data(fullUrl)
|
||||||
|
.crossfade(true)
|
||||||
|
.build(),
|
||||||
contentDescription = "用户头像",
|
contentDescription = "用户头像",
|
||||||
tint = LightPurple,
|
contentScale = ContentScale.Crop,
|
||||||
modifier = Modifier.size(size * 0.6f)
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.clip(CircleShape)
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
Icon(
|
Icon(
|
||||||
|
|||||||
Reference in New Issue
Block a user