首页Agent卡片组件

This commit is contained in:
2025-09-10 18:02:58 +08:00
parent c41c097d41
commit 922d6e72d6
2 changed files with 219 additions and 131 deletions

View File

@@ -4,10 +4,10 @@
<selectionStates> <selectionStates>
<SelectionState runConfigName="app"> <SelectionState runConfigName="app">
<option name="selectionMode" value="DROPDOWN" /> <option name="selectionMode" value="DROPDOWN" />
<DropdownSelection timestamp="2025-09-08T06:52:32.669239Z"> <DropdownSelection timestamp="2025-09-09T09:51:06.656104400Z">
<Target type="DEFAULT_BOOT"> <Target type="DEFAULT_BOOT">
<handle> <handle>
<DeviceId pluginId="LocalEmulator" identifier="path=/Users/liudikang/.android/avd/Pixel_8_API_30.avd" /> <DeviceId pluginId="Default" identifier="serial=192.168.0.227:45035;connection=094cb92e" />
</handle> </handle>
</Target> </Target>
</DropdownSelection> </DropdownSelection>

View File

@@ -16,6 +16,7 @@ import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.navigationBars import androidx.compose.foundation.layout.navigationBars
import androidx.compose.foundation.layout.offset
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.systemBars import androidx.compose.foundation.layout.systemBars
@@ -67,6 +68,12 @@ import com.aiosman.ravenow.ui.index.tabs.moment.tabs.expolre.ExploreViewModel
import com.aiosman.ravenow.utils.DebounceUtils import com.aiosman.ravenow.utils.DebounceUtils
import com.aiosman.ravenow.utils.ResourceCleanupManager import com.aiosman.ravenow.utils.ResourceCleanupManager
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import androidx.compose.foundation.lazy.LazyRow
import androidx.compose.foundation.lazy.grid.GridCells
import androidx.compose.foundation.lazy.grid.LazyVerticalGrid
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.foundation.lazy.grid.items
@OptIn( ExperimentalFoundationApi::class) @OptIn( ExperimentalFoundationApi::class)
@Composable @Composable
@@ -82,6 +89,7 @@ fun Agent() {
var scope = rememberCoroutineScope() var scope = rememberCoroutineScope()
val viewModel: AgentViewModel = viewModel() val viewModel: AgentViewModel = viewModel()
val scrollState = rememberScrollState()
// 确保推荐Agent数据已加载 // 确保推荐Agent数据已加载
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
@@ -102,6 +110,7 @@ fun Agent() {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.verticalScroll(scrollState)
.padding( .padding(
top = statusBarPaddingValues.calculateTopPadding(), top = statusBarPaddingValues.calculateTopPadding(),
bottom = navigationBarPaddings, bottom = navigationBarPaddings,
@@ -196,7 +205,6 @@ fun Agent() {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(400.dp)
.padding(vertical = 8.dp) .padding(vertical = 8.dp)
) { ) {
@@ -221,73 +229,95 @@ fun Agent() {
// ) // )
// } // }
var selectedTabIndex by remember { mutableStateOf(0) }
// 标签页 // 标签页
Row( LazyRow(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.wrapContentHeight() .wrapContentHeight()
.padding( bottom = 16.dp), .padding(bottom = 16.dp),
horizontalArrangement = Arrangement.Start, horizontalArrangement = Arrangement.Start,
verticalAlignment = Alignment.Bottom verticalAlignment = Alignment.CenterVertically
) { ) {
// 推荐标签(默认选中) item {
CustomTabItem( CustomTabItem(
text = stringResource(R.string.agent_recommend), text = stringResource(R.string.agent_recommend),
isSelected = true, isSelected = selectedTabIndex == 0,
onClick = { onClick = {
// TODO: 实现点击切换逻辑 selectedTabIndex = 0
} }
) )
}
TabSpacer() item {
TabSpacer()
}
// Scenes标签 item {
CustomTabItem( CustomTabItem(
text = "scenes", text = "scenes",
isSelected = false, isSelected = selectedTabIndex == 1,
onClick = { onClick = {
// TODO: 实现点击切换逻辑 selectedTabIndex = 1
} }
) )
}
TabSpacer() item {
TabSpacer()
}
// Voices标签 item {
CustomTabItem( CustomTabItem(
text = "voices", text = "voices",
isSelected = false, isSelected = selectedTabIndex == 2,
onClick = { onClick = {
// TODO: 实现点击切换逻辑 selectedTabIndex = 2
} }
) )
}
TabSpacer() item {
TabSpacer()
}
// Anime标签 item {
CustomTabItem( CustomTabItem(
text = "anime", text = "anime",
isSelected = false, isSelected = selectedTabIndex == 3,
onClick = { onClick = {
// TODO: 实现点击切换逻辑 selectedTabIndex = 3
} }
) )
}
// TabSpacer() item {
// TabSpacer()
// // Assist标签 }
// CustomTabItem(
// text = "assist", item {
// isSelected = false, CustomTabItem(
// onClick = { text = "assist",
// // TODO: 实现点击切换逻辑 isSelected = selectedTabIndex == 4,
// } onClick = {
// ) selectedTabIndex = 4
}
)
}
}
when (selectedTabIndex) {
0 -> {
AgentViewPagerSection(agentItems = viewModel.agentItems.take(15), viewModel)
}
else -> {
val shuffledAgents = viewModel.agentItems.shuffled().take(15)
AgentViewPagerSection(agentItems = shuffledAgents, viewModel)
}
} }
// Agent ViewPager
AgentViewPagerSection(agentItems = viewModel.agentItems.take(15),viewModel)
} }
Spacer(modifier = Modifier.height(0.dp))
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -309,92 +339,150 @@ fun Agent() {
fontWeight = androidx.compose.ui.text.font.FontWeight.W600, fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
color = AppColors.text color = AppColors.text
) )
Spacer(modifier = Modifier.weight(1f))
// 只有非游客用户才显示"我的Agent"tab
if (!AppStore.isGuest) {
TabItem(
text = stringResource(R.string.agent_mine),
isSelected = pagerState.currentPage == 0,
onClick = {
if (DebounceUtils.simpleDebounceClick(lastClickTime, 300L) {
scope.launch {
pagerState.animateScrollToPage(0)
}
}) {
lastClickTime = System.currentTimeMillis()
}
}
)
TabSpacer()
}
TabItem(
text = stringResource(R.string.agent_hot),
isSelected = if (AppStore.isGuest) pagerState.currentPage == 0 else pagerState.currentPage == 1,
onClick = {
if (DebounceUtils.simpleDebounceClick(lastClickTime, 300L) {
scope.launch {
val targetPage = if (AppStore.isGuest) 0 else 1
pagerState.animateScrollToPage(targetPage)
}
}) {
lastClickTime = System.currentTimeMillis()
}
}
)
/*TabSpacer()
TabItem(
text = stringResource(R.string.agent_recommend),
isSelected = pagerState.currentPage == 2,
onClick = {
scope.launch {
pagerState.animateScrollToPage(2)
}
}
)
TabSpacer()
TabItem(
text = stringResource(R.string.agent_other),
isSelected = pagerState.currentPage == 3,
onClick = {
scope.launch {
pagerState.animateScrollToPage(3)
}
}
)*/
} }
Spacer(modifier = Modifier.height(16.dp)) Spacer(modifier = Modifier.height(50.dp))
HorizontalPager( Column(
state = pagerState,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.weight(1f), .weight(1f)
beyondBoundsPageCount = 1 // 预加载相邻页面,避免切换时重新加载
) { ) {
if (AppStore.isGuest) { val agentItems = viewModel.agentItems.take(15)
// 游客模式下只显示热门Agent LazyVerticalGrid(
when (it) { columns = GridCells.Fixed(2),
0 -> { modifier = Modifier.fillMaxWidth(),
HotAgent() horizontalArrangement = Arrangement.spacedBy(16.dp),
} verticalArrangement = Arrangement.spacedBy(50.dp)
} ) {
} else { items(agentItems) { agentItem ->
// 正常用户显示我的Agent和热门Agent AgentCardSquare(
when (it) { agentItem = agentItem,
0 -> { viewModel = viewModel,
MineAgent() navController = LocalNavController.current
} )
1 -> {
HotAgent()
}
} }
} }
} }
} }
} }
@SuppressLint("SuspiciousIndentation")
@Composable
fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navController: NavHostController) {
val AppColors = LocalAppTheme.current
val cardHeight = 180.dp
val avatarSize = cardHeight / 3 // 头像大小为方块高度的三分之一
// 防抖状态
var lastClickTime by remember { mutableStateOf(0L) }
Box(
modifier = Modifier
.fillMaxWidth()
.height(cardHeight)
.background(Color(0xFFE0E0E0), RoundedCornerShape(12.dp)) // 灰色背景
.clickable {
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
viewModel.goToProfile(agentItem.openId, navController)
}) {
lastClickTime = System.currentTimeMillis()
}
},
contentAlignment = Alignment.TopCenter
) {
// 头像,位于方块上方居中,部分悬于方块外部
Box(
modifier = Modifier
.offset(y = -avatarSize / 2)
.size(avatarSize)
.background(Color.White, RoundedCornerShape(avatarSize / 2))
.clip(RoundedCornerShape(avatarSize / 2)),
contentAlignment = Alignment.Center
) {
if (agentItem.avatar.isNotEmpty()) {
CustomAsyncImage(
imageUrl = agentItem.avatar,
contentDescription = "Agent头像",
modifier = Modifier
.size(avatarSize)
.clip(RoundedCornerShape(avatarSize / 2)),
contentScale = androidx.compose.ui.layout.ContentScale.Crop
)
} else {
Image(
painter = painterResource(R.mipmap.rider_pro_agent),
contentDescription = "默认头像",
modifier = Modifier.size(avatarSize / 2),
colorFilter = ColorFilter.tint(AppColors.secondaryText)
)
}
}
// 内容区域(名称和描述)
Column(
modifier = Modifier
.fillMaxWidth()
.padding(top = avatarSize / 2 + 8.dp, start = 8.dp, end = 8.dp, bottom = 8.dp),
horizontalAlignment = Alignment.CenterHorizontally
) {
androidx.compose.material3.Text(
text = agentItem.title,
fontSize = 16.sp,
fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
color = AppColors.text,
maxLines = 1,
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis
)
Spacer(modifier = Modifier.height(8.dp))
androidx.compose.material3.Text(
text = agentItem.desc,
fontSize = 14.sp,
color = AppColors.secondaryText,
maxLines = 2,
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
modifier = Modifier.weight(1f, fill = false)
)
Spacer(modifier = Modifier.height(8.dp))
// 聊天按钮,位于底部居中
Box(
modifier = Modifier
.width(80.dp)
.height(32.dp)
.background(
color = Color(0X147c7480),
shape = RoundedCornerShape(8.dp)
)
.clickable {
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
// 检查游客模式,如果是游客则跳转登录
if (GuestLoginCheckOut.needLogin(GuestLoginCheckOutScene.CHAT_WITH_AGENT)) {
navController.navigate(NavigationRoute.Login.route)
} else {
viewModel.createSingleChat(agentItem.openId)
viewModel.goToChatAi(
agentItem.openId,
navController = navController
)
}
}) {
lastClickTime = System.currentTimeMillis()
}
},
contentAlignment = Alignment.Center
) {
androidx.compose.material3.Text(
text = stringResource(R.string.chat),
fontSize = 12.sp,
color = AppColors.text,
fontWeight = androidx.compose.ui.text.font.FontWeight.W500
)
}
}
}
}
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class)
@Composable @Composable
fun AgentViewPagerSection(agentItems: List<AgentItem>,viewModel: AgentViewModel) { fun AgentViewPagerSection(agentItems: List<AgentItem>,viewModel: AgentViewModel) {