Merge pull request #22 from Zhong202501/main

添加Category接口
This commit is contained in:
2025-09-17 10:39:22 +08:00
committed by GitHub
5 changed files with 288 additions and 130 deletions

View File

@@ -271,6 +271,44 @@ data class RemoveAccountRequestBody(
val password: String, val password: String,
) )
data class CategoryTemplate(
@SerializedName("id")
val id: Int,
@SerializedName("name")
val name: String,
@SerializedName("description")
val description: String,
@SerializedName("avatar")
val avatar: String,
@SerializedName("parentId")
val parentId: Int?,
@SerializedName("parent")
val parent: CategoryTemplate?,
@SerializedName("children")
val children: List<CategoryTemplate>?,
@SerializedName("sort")
val sort: Int,
@SerializedName("isActive")
val isActive: Boolean,
@SerializedName("promptCount")
val promptCount: Int?,
@SerializedName("createdAt")
val createdAt: String,
@SerializedName("updatedAt")
val updatedAt: String
)
data class CategoryListResponse(
@SerializedName("page")
val page: Int,
@SerializedName("pageSize")
val pageSize: Int,
@SerializedName("total")
val total: Int,
@SerializedName("list")
val list: List<CategoryTemplate>
)
interface RaveNowAPI { interface RaveNowAPI {
@GET("membership/config") @GET("membership/config")
@retrofit2.http.Headers("X-Requires-Auth: true") @retrofit2.http.Headers("X-Requires-Auth: true")
@@ -552,6 +590,7 @@ interface RaveNowAPI {
@Query("pageSize") pageSize: Int = 20, @Query("pageSize") pageSize: Int = 20,
@Query("withWorkflow") withWorkflow: Int = 1, @Query("withWorkflow") withWorkflow: Int = 1,
@Query("authorId") authorId: Int? = null, @Query("authorId") authorId: Int? = null,
@Query("categoryIds") categoryIds: List<Int>? = null,
): Response<DataContainer<ListContainer<Agent>>> ): Response<DataContainer<ListContainer<Agent>>>
@GET("outside/my/prompts") @GET("outside/my/prompts")
@@ -605,7 +644,38 @@ interface RaveNowAPI {
suspend fun joinRoom(@Body body: JoinGroupChatRequestBody, suspend fun joinRoom(@Body body: JoinGroupChatRequestBody,
): Response<DataContainer<Room>> ): Response<DataContainer<Room>>
@GET("outside/categories")
suspend fun getCategories(
@Query("page") page: Int? = null,
@Query("pageSize") pageSize: Int? = null,
@Query("parentId") parentId: Int? = null,
@Query("isActive") isActive: Boolean? = null,
@Query("name") name: String? = null,
@Query("withChildren") withChildren: Boolean? = null,
@Query("withParent") withParent: Boolean? = null,
@Query("withCount") withCount: Boolean? = null,
@Query("hideEmpty") hideEmpty: Boolean? = null
): Response<DataContainer<CategoryListResponse>>
@GET("outside/categories/tree")
suspend fun getCategoryTree(
@Query("withCount") withCount: Boolean? = null,
@Query("hideEmpty") hideEmpty: Boolean? = null
): Response<DataContainer<List<CategoryTemplate>>>
@GET("outside/categories/{id}")
suspend fun getCategoryById(
@Path("id") id: Int
): Response<DataContainer<CategoryTemplate>>
@GET("outside/prompts")
suspend fun getPromptsByCategory(
@Query("categoryIds") categoryIds: List<Int>? = null,
@Query("categoryName") categoryName: String? = null,
@Query("uncategorized") uncategorized: String? = null,
@Query("page") page: Int? = null,
@Query("pageSize") pageSize: Int? = null
): Response<ListContainer<Agent>>
} }

View File

@@ -283,7 +283,7 @@ fun ChatAiScreen(userId: String) {
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
.background(AppColors.decentBackground) .background(Color.White)
.padding(paddingValues) .padding(paddingValues)
) { ) {
LazyColumn( LazyColumn(

View File

@@ -246,6 +246,7 @@ fun Agent() {
isSelected = selectedTabIndex == 0, isSelected = selectedTabIndex == 0,
onClick = { onClick = {
selectedTabIndex = 0 selectedTabIndex = 0
viewModel.loadAllAgents()
} }
) )
} }
@@ -254,6 +255,24 @@ fun Agent() {
TabSpacer() TabSpacer()
} }
// 动态添加分类标签
viewModel.categories.take(4).forEachIndexed { index, category ->
item {
CustomTabItem(
text = category.name,
isSelected = selectedTabIndex == index + 1,
onClick = {
selectedTabIndex = index + 1
viewModel.loadAgentsByCategory(category.id)
}
)
}
item {
TabSpacer()
}
}
item { item {
CustomTabItem( CustomTabItem(
text = "scenes", text = "scenes",
@@ -271,9 +290,9 @@ fun Agent() {
item { item {
CustomTabItem( CustomTabItem(
text = "voices", text = "voices",
isSelected = selectedTabIndex == 2, isSelected = selectedTabIndex == 6,
onClick = { onClick = {
selectedTabIndex = 2 selectedTabIndex = 6
} }
) )
} }
@@ -285,9 +304,9 @@ fun Agent() {
item { item {
CustomTabItem( CustomTabItem(
text = "anime", text = "anime",
isSelected = selectedTabIndex == 3, isSelected = selectedTabIndex == 7,
onClick = { onClick = {
selectedTabIndex = 3 selectedTabIndex = 7
} }
) )
} }
@@ -299,16 +318,19 @@ fun Agent() {
item { item {
CustomTabItem( CustomTabItem(
text = "assist", text = "assist",
isSelected = selectedTabIndex == 4, isSelected = selectedTabIndex == 8,
onClick = { onClick = {
selectedTabIndex = 4 selectedTabIndex = 8
} }
) )
} }
} }
when (selectedTabIndex) { when {
0 -> { selectedTabIndex == 0 -> {
AgentViewPagerSection(agentItems = viewModel.agentItems.take(15), viewModel)
}
selectedTabIndex in 1..viewModel.categories.size -> {
AgentViewPagerSection(agentItems = viewModel.agentItems.take(15), viewModel) AgentViewPagerSection(agentItems = viewModel.agentItems.take(15), viewModel)
} }
else -> { else -> {
@@ -341,18 +363,18 @@ fun Agent() {
) )
} }
Spacer(modifier = Modifier.height(50.dp)) Spacer(modifier = Modifier.height(16.dp))
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.weight(1f) .weight(1f)
) { ) {
val agentItems = viewModel.agentItems.take(15) val agentItems = viewModel.agentItems
LazyVerticalGrid( LazyVerticalGrid(
columns = GridCells.Fixed(2), columns = GridCells.Fixed(2),
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(16.dp), horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalArrangement = Arrangement.spacedBy(50.dp) verticalArrangement = Arrangement.spacedBy(32.dp)
) { ) {
items(agentItems) { agentItem -> items(agentItems) { agentItem ->
AgentCardSquare( AgentCardSquare(
@@ -369,7 +391,7 @@ fun Agent() {
@Composable @Composable
fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navController: NavHostController) { fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navController: NavHostController) {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
val cardHeight = 180.dp val cardHeight = 200.dp
val avatarSize = cardHeight / 3 // 头像大小为方块高度的三分之一 val avatarSize = cardHeight / 3 // 头像大小为方块高度的三分之一
// 防抖状态 // 防抖状态
@@ -378,8 +400,9 @@ fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navControll
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.padding(top = avatarSize / 2)
.height(cardHeight) .height(cardHeight)
.background(Color(0xFFE0E0E0), RoundedCornerShape(12.dp)) // 灰色背景 .background(AppColors.nonActive, RoundedCornerShape(12.dp)) // 修改背景颜色
.clickable { .clickable {
if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) { if (DebounceUtils.simpleDebounceClick(lastClickTime, 500L) {
viewModel.goToProfile(agentItem.openId, navController) viewModel.goToProfile(agentItem.openId, navController)
@@ -426,7 +449,7 @@ fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navControll
) { ) {
androidx.compose.material3.Text( androidx.compose.material3.Text(
text = agentItem.title, text = agentItem.title,
fontSize = 16.sp, fontSize = 14.sp,
fontWeight = androidx.compose.ui.text.font.FontWeight.W600, fontWeight = androidx.compose.ui.text.font.FontWeight.W600,
color = AppColors.text, color = AppColors.text,
maxLines = 1, maxLines = 1,
@@ -435,21 +458,26 @@ fun AgentCardSquare(agentItem: AgentItem, viewModel: AgentViewModel, navControll
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
Box(
modifier = Modifier
.height(85.dp)
.fillMaxWidth()
) {
androidx.compose.material3.Text( androidx.compose.material3.Text(
text = agentItem.desc, text = agentItem.desc,
fontSize = 14.sp, fontSize = 12.sp,
color = AppColors.secondaryText, color = AppColors.secondaryText,
maxLines = 2, maxLines = 5,
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis, overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
modifier = Modifier.weight(1f, fill = false)
) )
}
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
// 聊天按钮,位于底部居中 // 聊天按钮,位于底部居中
Box( Box(
modifier = Modifier modifier = Modifier
.width(80.dp) .width(60.dp)
.height(32.dp) .height(32.dp)
.background( .background(
color = Color(0X147c7480), color = Color(0X147c7480),

View File

@@ -6,7 +6,10 @@ import androidx.compose.runtime.setValue
import androidx.lifecycle.ViewModel import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.navigation.NavHostController import androidx.navigation.NavHostController
import com.aiosman.ravenow.data.Agent
import com.aiosman.ravenow.data.ListContainer
import com.aiosman.ravenow.data.api.ApiClient import com.aiosman.ravenow.data.api.ApiClient
import com.aiosman.ravenow.data.api.CategoryTemplate
import com.aiosman.ravenow.data.api.RaveNowAPI import com.aiosman.ravenow.data.api.RaveNowAPI
import com.aiosman.ravenow.data.api.SingleChatRequestBody import com.aiosman.ravenow.data.api.SingleChatRequestBody
import com.aiosman.ravenow.ui.index.tabs.ai.tabs.mine.MineAgentViewModel.createGroup2ChatAi import com.aiosman.ravenow.ui.index.tabs.ai.tabs.mine.MineAgentViewModel.createGroup2ChatAi
@@ -22,6 +25,8 @@ object AgentViewModel: ViewModel() {
var agentItems by mutableStateOf<List<AgentItem>>(emptyList()) var agentItems by mutableStateOf<List<AgentItem>>(emptyList())
private set private set
var categories by mutableStateOf<List<CategoryItem>>(emptyList())
private set
var errorMessage by mutableStateOf<String?>(null) var errorMessage by mutableStateOf<String?>(null)
private set private set
@@ -35,16 +40,30 @@ object AgentViewModel: ViewModel() {
init { init {
loadAgentData() loadAgentData()
loadCategories()
} }
private fun loadAgentData() { private fun loadAgentData(categoryId: Int? = null) {
viewModelScope.launch { viewModelScope.launch {
isLoading = true isLoading = true
errorMessage = null errorMessage = null
try { try {
val response = apiClient.getAgent(page = 1, pageSize = 20, withWorkflow = 1) val response = if (categoryId != null) {
// 根据分类ID获取智能体
apiClient.getAgent(
page = 1,
pageSize = 20,
withWorkflow = 1,
categoryIds = listOf(categoryId)
)
} else {
// 获取所有智能体
apiClient.getAgent(page = 1, pageSize = 20, withWorkflow = 1)
}
if (response.isSuccessful) { if (response.isSuccessful) {
val agents = response.body()?.data?.list ?: emptyList() val agents = response.body()?.data?.list ?: emptyList<Agent>()
agentItems = agents.map { agent -> agentItems = agents.map { agent ->
AgentItem.fromAgent(agent) AgentItem.fromAgent(agent)
} }
@@ -58,6 +77,44 @@ object AgentViewModel: ViewModel() {
} }
} }
} }
private fun loadCategories() {
viewModelScope.launch {
try {
val response = apiClient.getCategories(
pageSize = 20,
withChildren = false,
withParent = false,
withCount = true,
hideEmpty = true
)
println("分类数据请求完成,响应成功: ${response.isSuccessful}")
if (response.isSuccessful) {
val categoryList = response.body()?.data?.list ?: emptyList()
println("获取到 ${categoryList.size} 个分类")
categories = categoryList.map { category ->
CategoryItem.fromCategoryTemplate(category)
}
println("成功处理并映射了 ${categories.size} 个分类")
} else {
errorMessage = "获取分类数据失败: ${response.code()}"
println("获取分类数据失败: ${response.code()}")
}
} catch (e: Exception) {
errorMessage = "获取分类数据失败: ${e.message}"
println("获取分类数据异常: ${e.message}")
e.printStackTrace()
}
}
}
fun loadAgentsByCategory(categoryId: Int) {
loadAgentData(categoryId)
}
fun loadAllAgents() {
loadAgentData()
}
fun createSingleChat( fun createSingleChat(
openId: String, openId: String,
) { ) {
@@ -122,3 +179,22 @@ object AgentViewModel: ViewModel() {
} }
} }
data class CategoryItem(
val id: Int,
val name: String,
val description: String,
val avatar: String,
val promptCount: Int?
) {
companion object {
fun fromCategoryTemplate(template: CategoryTemplate): CategoryItem {
return CategoryItem(
id = template.id,
name = template.name,
description = template.description,
avatar = "${ApiClient.BASE_API_URL}${template.avatar}",
promptCount = template.promptCount
)
}
}
}

View File

@@ -50,10 +50,12 @@ import androidx.compose.runtime.setValue
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.draw.drawWithContent
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.graphics.ColorFilter import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.graphics.graphicsLayer
import androidx.compose.ui.graphics.nativeCanvas
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.layout.onGloballyPositioned import androidx.compose.ui.layout.onGloballyPositioned
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
@@ -369,39 +371,6 @@ fun ProfileV3(
) )
} }
} }
if (isSelf&&isMain) {
Box(
modifier = Modifier
.align(Alignment.TopEnd)
.padding(
top = statusBarPaddingValues.calculateTopPadding(),
start = 8.dp,
end = 8.dp
)
.noRippleClickable {
IndexViewModel.openDrawer = true
}
) {
Box(
modifier = Modifier
.padding(16.dp)
.clip(RoundedCornerShape(8.dp))
.background(
AppColors.background.copy(
alpha = 0.7f
)
)
) {
Icon(
painter = painterResource(id = R.drawable.rider_pro_more_horizon),
contentDescription = "",
tint = AppColors.text
)
}
}
}
} }
Box( Box(
@@ -509,6 +478,7 @@ fun ProfileV3(
} }
Box(modifier = Modifier.fillMaxWidth()) {
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
@@ -566,7 +536,24 @@ fun ProfileV3(
Spacer(modifier = Modifier.weight(1f)) Spacer(modifier = Modifier.weight(1f))
if (isSelf&&isMain) { if (isSelf&&isMain) {
Box( Box(
modifier = Modifier.noRippleClickable { modifier = Modifier
.size(24.dp)
.padding(16.dp)
)
}
}
Spacer(modifier = Modifier.height(8.dp))
}
if (isSelf&&isMain) {
Box(
modifier = Modifier
.align(Alignment.TopEnd)
.padding(
top = 32.dp ,
end = 16.dp
)
.noRippleClickable {
IndexViewModel.openDrawer = true IndexViewModel.openDrawer = true
} }
) { ) {
@@ -584,9 +571,6 @@ fun ProfileV3(
} }
} }
}
Spacer(modifier = Modifier.height(8.dp))
} }
} }
) { ) {