create agent

This commit is contained in:
weber
2025-08-05 16:25:06 +08:00
parent 0f5d3d7960
commit daea7824af
8 changed files with 195 additions and 78 deletions

View File

@@ -22,7 +22,7 @@ import java.io.IOException
suspend fun createAgent( suspend fun createAgent(
title: String, title: String,
desc: String, desc: String,
avatar: UploadImage, avatar: UploadImage? = null,
): AgentEntity { ): AgentEntity {
val textTitle = title.toRequestBody("text/plain".toMediaTypeOrNull()) val textTitle = title.toRequestBody("text/plain".toMediaTypeOrNull())
val textDesc = desc.toRequestBody("text/plain".toMediaTypeOrNull()) val textDesc = desc.toRequestBody("text/plain".toMediaTypeOrNull())

View File

@@ -15,7 +15,9 @@ import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.filled.Add import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Check import androidx.compose.material.icons.filled.Check
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -31,6 +33,7 @@ import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import com.aiosman.ravenow.LocalAppTheme import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.LocalNavController import com.aiosman.ravenow.LocalNavController
@@ -58,12 +61,11 @@ fun AddAgentScreen() {
val context = LocalContext.current val context = LocalContext.current
var agnetNameError by remember { mutableStateOf<String?>(null) } var agnetNameError by remember { mutableStateOf<String?>(null) }
var agnetDescError by remember { mutableStateOf<String?>(null) } var agnetDescError by remember { mutableStateOf<String?>(null) }
fun onNicknameChange(value: String) { var errorMessage by remember { mutableStateOf<String?>(null) }
fun onNameChange(value: String) {
model.name = value model.name = value
agnetNameError = when { agnetNameError = when {
/*value.isEmpty() -> "昵称不能为空"
value.length < 3 -> "昵称长度不能小于3"
value.length > 20 -> "昵称长度不能大于20"*/
else -> null else -> null
} }
} }
@@ -73,7 +75,7 @@ fun AddAgentScreen() {
fun onDescChange(value: String) { fun onDescChange(value: String) {
model.desc = value model.desc = value
agnetDescError = when { agnetDescError = when {
value.length > 100 -> "个人简介长度不能大于24" value.length > 100 -> "简介长度不能大于100"
else -> null else -> null
} }
} }
@@ -115,7 +117,7 @@ fun AddAgentScreen() {
) { ) {
CustomAsyncImage( CustomAsyncImage(
context, context,
"", model.croppedBitmap,
modifier = Modifier modifier = Modifier
.size(88.dp) .size(88.dp)
.clip( .clip(
@@ -125,6 +127,10 @@ fun AddAgentScreen() {
contentScale = ContentScale.Crop, contentScale = ContentScale.Crop,
placeholderRes = R.mipmap.rider_pro_agent_avatar placeholderRes = R.mipmap.rider_pro_agent_avatar
) )
// 调试信息
LaunchedEffect(model.croppedBitmap) {
println("AddAgent: croppedBitmap changed: ${model.croppedBitmap != null}")
}
Box( Box(
modifier = Modifier modifier = Modifier
.size(32.dp) .size(32.dp)
@@ -132,6 +138,7 @@ fun AddAgentScreen() {
.background(appColors.main) .background(appColors.main)
.align(Alignment.BottomEnd) .align(Alignment.BottomEnd)
.noRippleClickable { .noRippleClickable {
model.isFromAddAgent = true
navController.navigate(NavigationRoute.ImageCrop.route) navController.navigate(NavigationRoute.ImageCrop.route)
}, },
contentAlignment = Alignment.Center contentAlignment = Alignment.Center
@@ -156,7 +163,7 @@ fun AddAgentScreen() {
background = appColors.inputBackground2, background = appColors.inputBackground2,
modifier = Modifier.fillMaxWidth(), modifier = Modifier.fillMaxWidth(),
) { value -> ) { value ->
onNicknameChange(value) onNameChange(value)
} }
// Spacer(modifier = Modifier.height(16.dp)) // Spacer(modifier = Modifier.height(16.dp))
FormTextInput2( FormTextInput2(
@@ -170,6 +177,18 @@ fun AddAgentScreen() {
} }
} }
Spacer(modifier = Modifier.height(58.dp)) Spacer(modifier = Modifier.height(58.dp))
// 错误信息显示
errorMessage?.let { error ->
Text(
text = error,
color = Color.Red,
modifier = Modifier.padding(horizontal = 16.dp),
fontSize = 14.sp
)
Spacer(modifier = Modifier.height(16.dp))
}
ActionButton( ActionButton(
modifier = Modifier modifier = Modifier
.width(345.dp) .width(345.dp)
@@ -187,10 +206,40 @@ fun AddAgentScreen() {
color = Color.White, color = Color.White,
backgroundColor = Color.Transparent, backgroundColor = Color.Transparent,
text = stringResource(R.string.agent_create), text = stringResource(R.string.agent_create),
isLoading = model.isUpdating,
loadingText = "创建中...",
enabled = !model.isUpdating && model.validate() == null
) { ) {
// 验证输入
val validationError = model.validate()
} if (validationError != null) {
// 显示验证错误
errorMessage = validationError
println("AddAgent: Validation error: $validationError")
return@ActionButton
}
// 清除之前的错误信息
errorMessage = null
// 调用创建智能体API
model.viewModelScope.launch {
try {
println("AddAgent: Starting to create agent...")
val result = model.createAgent(context)
if (result != null) {
println("AddAgent: Agent created successfully, closing page")
// 创建成功,关闭页面
navController.popBackStack()
}
} catch (e: Exception) {
println("AddAgent: Error creating agent: ${e.message}")
// 显示错误信息
errorMessage = "创建智能体失败: ${e.message}"
e.printStackTrace()
}
}
}
} }

View File

@@ -10,7 +10,10 @@ import androidx.lifecycle.ViewModel
import com.aiosman.ravenow.data.AccountService import com.aiosman.ravenow.data.AccountService
import com.aiosman.ravenow.data.AccountServiceImpl import com.aiosman.ravenow.data.AccountServiceImpl
import com.aiosman.ravenow.data.UploadImage import com.aiosman.ravenow.data.UploadImage
import com.aiosman.ravenow.data.ServiceException
import com.aiosman.ravenow.entity.AccountProfileEntity import com.aiosman.ravenow.entity.AccountProfileEntity
import com.aiosman.ravenow.entity.AgentEntity
import com.aiosman.ravenow.entity.createAgent
import com.aiosman.ravenow.ui.index.tabs.profile.MyProfileViewModel import com.aiosman.ravenow.ui.index.tabs.profile.MyProfileViewModel
import com.aiosman.ravenow.utils.TrtcHelper import com.aiosman.ravenow.utils.TrtcHelper
import java.io.File import java.io.File
@@ -18,8 +21,60 @@ import java.io.File
object AddAgentViewModel : ViewModel() { object AddAgentViewModel : ViewModel() {
var name by mutableStateOf("") var name by mutableStateOf("")
var desc by mutableStateOf("") var desc by mutableStateOf("")
var imageUrl by mutableStateOf<Uri?>(null)
var croppedBitmap by mutableStateOf<Bitmap?>(null) var croppedBitmap by mutableStateOf<Bitmap?>(null)
var isUpdating by mutableStateOf(false) var isUpdating by mutableStateOf(false)
var isFromAddAgent by mutableStateOf(false)
suspend fun updateAgentAvatar(context: Context) {
println("AddAgentViewModel: updateAgentAvatar called, croppedBitmap: ${croppedBitmap != null}")
croppedBitmap?.let {
val file = File(context.cacheDir, "agent_avatar.jpg")
it.compress(Bitmap.CompressFormat.JPEG, 100, file.outputStream())
println("AddAgentViewModel: Avatar saved to ${file.absolutePath}")
// 这里可以上传图片到服务器,暂时先保存到本地
// UploadImage(file, "agent_avatar.jpg", "", "jpg")
}
}
suspend fun createAgent(context: Context): AgentEntity? {
try {
isUpdating = true
println("AddAgentViewModel: Creating agent with name: $name, desc: $desc")
// 准备头像文件
val avatarFile = if (croppedBitmap != null) {
val file = File(context.cacheDir, "agent_avatar.jpg")
croppedBitmap!!.compress(Bitmap.CompressFormat.JPEG, 100, file.outputStream())
UploadImage(file, "agent_avatar.jpg", "", "jpg")
} else {
null
}
// 调用API创建智能体
val result = createAgent(
title = name,
desc = desc,
avatar = avatarFile
)
println("AddAgentViewModel: Agent created successfully with ID: ${result.id}")
return result
} catch (e: Exception) {
println("AddAgentViewModel: Error creating agent: ${e.message}")
throw e
} finally {
isUpdating = false
}
}
fun validate(): String? {
return when {
name.isEmpty() -> "智能体名称不能为空"
name.length < 2 -> "智能体名称长度不能少于2个字符"
name.length > 20 -> "智能体名称长度不能超过20个字符"
desc.isEmpty() -> "智能体描述不能为空"
desc.length > 100 -> "智能体描述长度不能超过100个字符"
else -> null
}
}
} }

View File

@@ -3,6 +3,7 @@ package com.aiosman.ravenow.ui.composables
import android.content.Context import android.content.Context
import android.graphics.Bitmap import android.graphics.Bitmap
import androidx.annotation.DrawableRes import androidx.annotation.DrawableRes
import androidx.compose.foundation.Image
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
@@ -10,6 +11,7 @@ import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.asImageBitmap
import androidx.compose.ui.layout.ContentScale import androidx.compose.ui.layout.ContentScale
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.core.graphics.drawable.toBitmap import androidx.core.graphics.drawable.toBitmap
@@ -57,11 +59,11 @@ fun CustomAsyncImage(
val imageLoader = getImageLoader(context ?: localContext) val imageLoader = getImageLoader(context ?: localContext)
// 处理 imageUrl 为 null 的情况 // 处理 imageUrl 为 null 或空字符串的情况
if (imageUrl == null|| imageUrl == "") { if (imageUrl == null || imageUrl == "") {
// 如果 imageUrl 为 null 且有占位符,则直接显示占位符 // 如果 imageUrl 为 null 且有占位符,则直接显示占位符
if (placeholderRes != null) { if (placeholderRes != null) {
androidx.compose.foundation.Image( Image(
painter = androidx.compose.ui.res.painterResource(placeholderRes), painter = androidx.compose.ui.res.painterResource(placeholderRes),
contentDescription = contentDescription, contentDescription = contentDescription,
modifier = modifier, modifier = modifier,
@@ -70,6 +72,19 @@ fun CustomAsyncImage(
return return
} }
} }
// 处理 Bitmap 类型
if (imageUrl is Bitmap) {
Image(
bitmap = imageUrl.asImageBitmap(),
contentDescription = contentDescription,
modifier = modifier,
contentScale = contentScale
)
return
}
// 处理字符串URL
AsyncImage( AsyncImage(
model = ImageRequest.Builder(context ?: localContext) model = ImageRequest.Builder(context ?: localContext)
.data(imageUrl) .data(imageUrl)

View File

@@ -40,6 +40,7 @@ import androidx.lifecycle.viewModelScope
import com.aiosman.ravenow.LocalNavController import com.aiosman.ravenow.LocalNavController
import com.aiosman.ravenow.R import com.aiosman.ravenow.R
import com.aiosman.ravenow.ui.account.AccountEditViewModel import com.aiosman.ravenow.ui.account.AccountEditViewModel
import com.aiosman.ravenow.ui.agent.AddAgentViewModel
import com.aiosman.ravenow.ui.composables.StatusBarSpacer import com.aiosman.ravenow.ui.composables.StatusBarSpacer
import com.google.accompanist.systemuicontroller.rememberSystemUiController import com.google.accompanist.systemuicontroller.rememberSystemUiController
import com.image.cropview.CropType import com.image.cropview.CropType
@@ -58,6 +59,7 @@ fun ImageCropScreen() {
var imageHeightInDp by remember { mutableStateOf(0) } var imageHeightInDp by remember { mutableStateOf(0) }
var density = LocalDensity.current var density = LocalDensity.current
var navController = LocalNavController.current var navController = LocalNavController.current
var imagePickLauncher = rememberLauncherForActivityResult( var imagePickLauncher = rememberLauncherForActivityResult(
contract = ActivityResultContracts.GetContent() contract = ActivityResultContracts.GetContent()
) { uri: Uri? -> ) { uri: Uri? ->
@@ -111,26 +113,29 @@ fun ImageCropScreen() {
modifier = Modifier.clickable { modifier = Modifier.clickable {
imageCrop?.let { imageCrop?.let {
val bitmap = it.onCrop() val bitmap = it.onCrop()
AccountEditViewModel.croppedBitmap = bitmap println("ImageCrop: Cropped bitmap created: ${bitmap != null}")
AccountEditViewModel.viewModelScope.launch { if (AddAgentViewModel.isFromAddAgent) {
AccountEditViewModel.updateUserProfile(context) println("ImageCrop: Setting bitmap to AddAgentViewModel")
navController.popBackStack() // 如果是从AddAgent页面跳转过来的
AddAgentViewModel.croppedBitmap = bitmap
AddAgentViewModel.viewModelScope.launch {
AddAgentViewModel.updateAgentAvatar(context)
AddAgentViewModel.isFromAddAgent = false
navController.popBackStack()
}
} else {
println("ImageCrop: Setting bitmap to AccountEditViewModel")
// 默认处理AccountEdit
AccountEditViewModel.croppedBitmap = bitmap
AccountEditViewModel.viewModelScope.launch {
AccountEditViewModel.updateUserProfile(context)
navController.popBackStack()
}
} }
} }
} }
) )
} }
// Spacer(
// modifier = Modifier.height(120.dp)
// )
// ActionButton(
// modifier = Modifier.fillMaxWidth(),
// text = "选择图片"
// ) {
// imagePickLauncher.launch("image/*")
// }
Box( Box(
modifier = Modifier.fillMaxWidth().padding(24.dp) modifier = Modifier.fillMaxWidth().padding(24.dp)
) { ) {
@@ -154,10 +159,7 @@ fun ImageCropScreen() {
) )
} }
} }
} }
} }

View File

@@ -226,19 +226,19 @@ fun Agent() {
) { ) {
when (it) { when (it) {
0 -> { 0 -> {
// ExploreMomentsList()
} }
1 -> { 1 -> {
//TimelineMomentsList()
} }
2 -> { 2 -> {
//HotMomentsList()
} }
3 -> { 3 -> {
//MineAgent()
} }
} }

View File

@@ -75,7 +75,6 @@ import com.aiosman.ravenow.AppState
import com.aiosman.ravenow.LocalAppTheme import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.LocalNavController import com.aiosman.ravenow.LocalNavController
import com.aiosman.ravenow.R import com.aiosman.ravenow.R
import com.aiosman.ravenow.entity.createAgent
import com.aiosman.ravenow.ui.NavigationRoute import com.aiosman.ravenow.ui.NavigationRoute
import com.aiosman.ravenow.ui.composables.CustomAsyncImage import com.aiosman.ravenow.ui.composables.CustomAsyncImage
import com.aiosman.ravenow.ui.composables.DraggableGrid import com.aiosman.ravenow.ui.composables.DraggableGrid

View File

@@ -696,9 +696,7 @@ fun Header(
onReportClick = { onReportClick = {
onReportClick() onReportClick()
expanded = false expanded = false
}, }
userId = userId,
isCurrentUser = AppState.UserId?.toInt() == userId
) )
} }
} }
@@ -1377,26 +1375,25 @@ fun PostBottomBar(
fun PostMenuModal( fun PostMenuModal(
onDeleteClick: () -> Unit = {}, onDeleteClick: () -> Unit = {},
onReportClick: () -> Unit = {}, onReportClick: () -> Unit = {},
userId: Int? = null, momentEntity: MomentEntity? = null
isCurrentUser: Boolean = false
) { ) {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(160.dp)
.background(AppColors.background) .background(AppColors.background)
.padding(vertical = 47.dp, horizontal = 20.dp) .padding(vertical = 47.dp, horizontal = 20.dp)
) { ) {
Row( Row(
modifier = Modifier modifier = Modifier
.fillMaxWidth(), .fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically, verticalAlignment = Alignment.CenterVertically
horizontalArrangement = Arrangement.Start
) { ) {
if (isCurrentUser) { momentEntity?.let {
// 显示删除选项给动态发布者
Column( Column(
modifier = Modifier.padding(end = 16.dp),
verticalArrangement = Arrangement.Center, verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally horizontalAlignment = Alignment.CenterHorizontally
) { ) {
@@ -1425,39 +1422,39 @@ fun PostMenuModal(
color = AppColors.text color = AppColors.text
) )
} }
} else {
// 显示举报选项给其他用户
Column(
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = Modifier
.clip(CircleShape)
.noRippleClickable {
onReportClick()
}
) {
Image(
painter = painterResource(id = R.drawable.rider_pro_moment_delete),
contentDescription = "",
modifier = Modifier.size(24.dp),
colorFilter = ColorFilter.tint(
AppColors.text
)
)
}
Spacer(modifier = Modifier.height(8.dp))
Text(
text = stringResource(R.string.report),
fontSize = 11.sp,
fontWeight = FontWeight.Bold,
color = AppColors.text
)
}
} }
} }
Column(
modifier = Modifier.padding(end = 16.dp),
verticalArrangement = Arrangement.Center,
horizontalAlignment = Alignment.CenterHorizontally
) {
Box(
modifier = Modifier
.clip(CircleShape)
.noRippleClickable {
onReportClick()
}
) {
Image(
painter = painterResource(id = R.drawable.rider_pro_moment_delete),
contentDescription = "",
modifier = Modifier.size(24.dp),
colorFilter = ColorFilter.tint(
AppColors.text
)
)
}
Spacer(modifier = Modifier.height(8.dp))
Text(
text = stringResource(R.string.report),
fontSize = 11.sp,
fontWeight = FontWeight.Bold,
color = AppColors.text
)
}
} }
} }