refactor: 优化聊天和回忆录相关UI组件
- 优化ChatHeader聊天头部组件 - 优化ChatInputField聊天输入组件 - 优化MessageList消息列表组件 - 重构BookInfoCard书籍信息卡片 - 优化ChapterCard章节卡片
This commit is contained in:
@@ -11,6 +11,7 @@ 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.graphics.Brush
|
||||||
import androidx.compose.ui.graphics.Color
|
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
|
||||||
@@ -20,17 +21,49 @@ import com.huaga.life_echo.ui.theme.LightPurple
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* 聊天头部组件
|
* 聊天头部组件
|
||||||
|
*
|
||||||
|
* @param title 标题
|
||||||
|
* @param isOnline 在线状态
|
||||||
|
* @param onBackClick 返回按钮点击回调
|
||||||
|
* @param modifier 修饰符
|
||||||
|
* @param isTransparent 是否透明化(默认false)
|
||||||
|
* @param transparencyType 透明化类型:0=完全透明, 1=半透明, 2=渐变透明
|
||||||
|
* @param alpha 透明度(0.0-1.0),用于半透明模式
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun ChatHeader(
|
fun ChatHeader(
|
||||||
title: String,
|
title: String,
|
||||||
isOnline: Boolean = true,
|
isOnline: Boolean = true,
|
||||||
onBackClick: () -> Unit,
|
onBackClick: () -> Unit,
|
||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier,
|
||||||
|
isTransparent: Boolean = false,
|
||||||
|
transparencyType: Int = 0, // 0=完全透明, 1=半透明, 2=渐变透明
|
||||||
|
alpha: Float = 0.5f
|
||||||
) {
|
) {
|
||||||
|
val backgroundColor = when {
|
||||||
|
isTransparent && transparencyType == 0 -> Color.Transparent // 完全透明
|
||||||
|
isTransparent && transparencyType == 1 -> LightPurple.copy(alpha = alpha) // 半透明
|
||||||
|
isTransparent && transparencyType == 2 -> Color.Transparent // 渐变透明(使用Brush)
|
||||||
|
else -> LightPurple // 不透明
|
||||||
|
}
|
||||||
|
|
||||||
|
val backgroundModifier = when {
|
||||||
|
isTransparent && transparencyType == 2 -> Modifier.background(
|
||||||
|
Brush.verticalGradient(
|
||||||
|
colors = listOf(
|
||||||
|
LightPurple.copy(alpha = 0.95f),
|
||||||
|
LightPurple.copy(alpha = 0.0f)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
)
|
||||||
|
else -> Modifier
|
||||||
|
}
|
||||||
|
|
||||||
Surface(
|
Surface(
|
||||||
color = LightPurple,
|
color = backgroundColor,
|
||||||
modifier = modifier.fillMaxWidth()
|
modifier = modifier
|
||||||
|
.fillMaxWidth()
|
||||||
|
.then(backgroundModifier)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
|
|||||||
@@ -14,7 +14,7 @@ import androidx.compose.ui.unit.sp
|
|||||||
import com.huaga.life_echo.ui.theme.LightPurple
|
import com.huaga.life_echo.ui.theme.LightPurple
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 聊天输入框组件(支持高度自适应)
|
* 聊天输入框组件(支持高度自适应和键盘自适应贴合)
|
||||||
*/
|
*/
|
||||||
@Composable
|
@Composable
|
||||||
fun ChatInputField(
|
fun ChatInputField(
|
||||||
@@ -27,13 +27,14 @@ fun ChatInputField(
|
|||||||
) {
|
) {
|
||||||
var textFieldHeight by remember { mutableStateOf(56.dp) }
|
var textFieldHeight by remember { mutableStateOf(56.dp) }
|
||||||
|
|
||||||
|
// 使用 windowInsetsPadding 实现键盘自适应贴合,确保输入框紧贴键盘
|
||||||
Surface(
|
Surface(
|
||||||
modifier = modifier
|
modifier = modifier
|
||||||
.fillMaxWidth()
|
.fillMaxWidth()
|
||||||
.shadow(4.dp)
|
.shadow(4.dp)
|
||||||
.windowInsetsPadding(WindowInsets.ime),
|
.windowInsetsPadding(WindowInsets.ime), // 自适应键盘高度,紧贴键盘
|
||||||
color = MaterialTheme.colorScheme.surface,
|
color = MaterialTheme.colorScheme.surface,
|
||||||
shape = RoundedCornerShape(topStart = 16.dp, topEnd = 16.dp)
|
shape = RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp)
|
||||||
) {
|
) {
|
||||||
Row(
|
Row(
|
||||||
modifier = Modifier
|
modifier = Modifier
|
||||||
@@ -64,7 +65,7 @@ fun ChatInputField(
|
|||||||
fontSize = 14.sp
|
fontSize = 14.sp
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
shape = RoundedCornerShape(28.dp),
|
shape = RoundedCornerShape(36.dp),
|
||||||
colors = OutlinedTextFieldDefaults.colors(
|
colors = OutlinedTextFieldDefaults.colors(
|
||||||
unfocusedBorderColor = Color.Transparent,
|
unfocusedBorderColor = Color.Transparent,
|
||||||
focusedBorderColor = LightPurple,
|
focusedBorderColor = LightPurple,
|
||||||
|
|||||||
@@ -6,7 +6,7 @@ import androidx.compose.foundation.lazy.LazyColumn
|
|||||||
import androidx.compose.foundation.lazy.LazyListState
|
import androidx.compose.foundation.lazy.LazyListState
|
||||||
import androidx.compose.foundation.lazy.rememberLazyListState
|
import androidx.compose.foundation.lazy.rememberLazyListState
|
||||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||||
import androidx.compose.material3.Divider
|
import androidx.compose.material3.HorizontalDivider
|
||||||
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
|
||||||
@@ -134,7 +134,7 @@ fun TimeDivider(
|
|||||||
verticalAlignment = Alignment.CenterVertically,
|
verticalAlignment = Alignment.CenterVertically,
|
||||||
horizontalArrangement = Arrangement.Center
|
horizontalArrangement = Arrangement.Center
|
||||||
) {
|
) {
|
||||||
Divider(
|
HorizontalDivider(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.2f)
|
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.2f)
|
||||||
)
|
)
|
||||||
@@ -146,7 +146,7 @@ fun TimeDivider(
|
|||||||
fontWeight = FontWeight.Normal
|
fontWeight = FontWeight.Normal
|
||||||
)
|
)
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
Spacer(modifier = Modifier.width(8.dp))
|
||||||
Divider(
|
HorizontalDivider(
|
||||||
modifier = Modifier.weight(1f),
|
modifier = Modifier.weight(1f),
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.2f)
|
color = MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.2f)
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -39,18 +39,13 @@ fun BookInfoCard(
|
|||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
var isEditingTitle by remember { mutableStateOf(false) }
|
var isEditingTitle by remember { mutableStateOf(false) }
|
||||||
var isEditingSubtitle by remember { mutableStateOf(false) }
|
|
||||||
var editedTitle by remember { mutableStateOf(book.title) }
|
var editedTitle by remember { mutableStateOf(book.title) }
|
||||||
var editedSubtitle by remember { mutableStateOf(book.subtitle ?: "") }
|
|
||||||
val keyboardController = LocalSoftwareKeyboardController.current
|
val keyboardController = LocalSoftwareKeyboardController.current
|
||||||
|
|
||||||
// 当book变化时更新编辑状态
|
// 当book变化时更新编辑状态
|
||||||
LaunchedEffect(book.title) {
|
LaunchedEffect(book.title) {
|
||||||
editedTitle = book.title
|
editedTitle = book.title
|
||||||
}
|
}
|
||||||
LaunchedEffect(book.subtitle) {
|
|
||||||
editedSubtitle = book.subtitle ?: ""
|
|
||||||
}
|
|
||||||
|
|
||||||
Card(
|
Card(
|
||||||
modifier = modifier.fillMaxWidth(),
|
modifier = modifier.fillMaxWidth(),
|
||||||
@@ -114,71 +109,35 @@ fun BookInfoCard(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(8.dp))
|
|
||||||
|
|
||||||
// 副标题(可选,可编辑)
|
|
||||||
if (book.subtitle != null || isEditingSubtitle) {
|
|
||||||
if (isEditingSubtitle) {
|
|
||||||
TextField(
|
|
||||||
value = editedSubtitle,
|
|
||||||
onValueChange = { editedSubtitle = it },
|
|
||||||
singleLine = true,
|
|
||||||
keyboardOptions = KeyboardOptions(imeAction = ImeAction.Done),
|
|
||||||
keyboardActions = KeyboardActions(
|
|
||||||
onDone = {
|
|
||||||
isEditingSubtitle = false
|
|
||||||
keyboardController?.hide()
|
|
||||||
onSubtitleChange?.invoke(if (editedSubtitle.isNotBlank()) editedSubtitle else null)
|
|
||||||
}
|
|
||||||
),
|
|
||||||
placeholder = {
|
|
||||||
Text("副标题(可选)")
|
|
||||||
},
|
|
||||||
colors = TextFieldDefaults.colors(
|
|
||||||
focusedTextColor = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
unfocusedTextColor = MaterialTheme.colorScheme.onSurfaceVariant
|
|
||||||
),
|
|
||||||
textStyle = MaterialTheme.typography.titleMedium
|
|
||||||
)
|
|
||||||
} else {
|
|
||||||
Row(
|
|
||||||
modifier = Modifier
|
|
||||||
.clickable { isEditingSubtitle = true }
|
|
||||||
.padding(vertical = 4.dp),
|
|
||||||
verticalAlignment = Alignment.CenterVertically
|
|
||||||
) {
|
|
||||||
Text(
|
|
||||||
text = book.subtitle ?: "",
|
|
||||||
fontSize = 18.sp,
|
|
||||||
color = MaterialTheme.colorScheme.primary
|
|
||||||
)
|
|
||||||
Spacer(modifier = Modifier.width(8.dp))
|
|
||||||
Icon(
|
|
||||||
imageVector = AppIcons.Edit,
|
|
||||||
contentDescription = "编辑",
|
|
||||||
tint = MaterialTheme.colorScheme.onSurfaceVariant,
|
|
||||||
modifier = Modifier.size(16.dp)
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// 添加副标题按钮
|
|
||||||
Text(
|
|
||||||
text = "+ 添加副标题",
|
|
||||||
fontSize = 14.sp,
|
|
||||||
color = MaterialTheme.colorScheme.primary.copy(alpha = 0.6f),
|
|
||||||
modifier = Modifier.clickable { isEditingSubtitle = true }
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
Spacer(modifier = Modifier.height(4.dp))
|
Spacer(modifier = Modifier.height(4.dp))
|
||||||
|
|
||||||
// 最后更新时间
|
// 书籍统计信息
|
||||||
|
Row(
|
||||||
|
modifier = Modifier.fillMaxWidth(),
|
||||||
|
horizontalArrangement = Arrangement.Center
|
||||||
|
) {
|
||||||
|
if (book.total_words != null && book.total_words > 0) {
|
||||||
Text(
|
Text(
|
||||||
text = TimeUtils.formatUpdateTime(book.lastUpdatedAt),
|
text = "${book.total_words}字",
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
if (book.total_pages != null && book.total_pages > 0) {
|
||||||
|
if (book.total_words != null && book.total_words > 0) {
|
||||||
|
Text(
|
||||||
|
text = " · ",
|
||||||
|
fontSize = 12.sp,
|
||||||
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
|
)
|
||||||
|
}
|
||||||
|
Text(
|
||||||
|
text = "${book.total_pages}页",
|
||||||
fontSize = 12.sp,
|
fontSize = 12.sp,
|
||||||
color = MaterialTheme.colorScheme.onSurfaceVariant
|
color = MaterialTheme.colorScheme.onSurfaceVariant
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
@@ -32,8 +32,8 @@ fun ChapterCard(
|
|||||||
modifier: Modifier = Modifier
|
modifier: Modifier = Modifier
|
||||||
) {
|
) {
|
||||||
val statusText = when (chapter.status) {
|
val statusText = when (chapter.status) {
|
||||||
"completed" -> "已整理${if (chapter.pageCount != null) " · 约${chapter.pageCount}页" else ""}"
|
"completed" -> "已整理"
|
||||||
"partial" -> "部分整理${if (chapter.pageCount != null) " · 约${chapter.pageCount}页" else ""}"
|
"partial" -> "部分整理"
|
||||||
else -> "待补充"
|
else -> "待补充"
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -62,7 +62,7 @@ fun ChapterCard(
|
|||||||
contentAlignment = Alignment.Center
|
contentAlignment = Alignment.Center
|
||||||
) {
|
) {
|
||||||
Text(
|
Text(
|
||||||
text = String.format("%02d", chapter.orderIndex),
|
text = String.format("%02d", chapter.order_index),
|
||||||
fontSize = 18.sp,
|
fontSize = 18.sp,
|
||||||
fontWeight = FontWeight.Bold,
|
fontWeight = FontWeight.Bold,
|
||||||
color = Color.White
|
color = Color.White
|
||||||
|
|||||||
Reference in New Issue
Block a user