feat: 新增智能体记忆管理功能
- 新增 Agent 记忆管理界面,允许用户对 Agent 的记忆进行增、删、改、查(CRUD)操作。 - 实现添加记忆的弹窗、记忆列表展示、编辑和删除功能。 - 在 Agent 个人资料页的操作菜单中添加入口,仅对自己创建的 Agent 可见。 - 集成积分系统,添加记忆需要消耗相应积分,并提供支付确认对话框。
This commit is contained in:
@@ -0,0 +1,806 @@
|
||||
package com.aiosman.ravenow.ui.profile
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.lazy.LazyColumn
|
||||
import androidx.compose.foundation.lazy.items
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.CircularProgressIndicator
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Brush
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.ColorFilter
|
||||
import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import android.widget.Toast
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.text.BasicTextField
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import com.aiosman.ravenow.LocalAppTheme
|
||||
import com.aiosman.ravenow.R
|
||||
import com.aiosman.ravenow.data.AgentRuleEntity
|
||||
import com.aiosman.ravenow.entity.AccountProfileEntity
|
||||
import com.aiosman.ravenow.ui.composables.CustomAsyncImage
|
||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||
|
||||
@Composable
|
||||
fun AgentMemoryManageContent(
|
||||
openId: String,
|
||||
profile: AccountProfileEntity?,
|
||||
viewModel: AgentMemoryManageViewModel,
|
||||
onAddMemoryClick: () -> Unit = {},
|
||||
onDismiss: () -> Unit = {}
|
||||
) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
val context = LocalContext.current
|
||||
|
||||
// 编辑记忆的状态 - 存储正在编辑的记忆ID
|
||||
var editingMemoryId by remember { mutableStateOf<Int?>(null) }
|
||||
|
||||
// 加载配额和列表数据
|
||||
LaunchedEffect(Unit) {
|
||||
viewModel.loadMemoryQuota()
|
||||
viewModel.loadMemoryList()
|
||||
}
|
||||
|
||||
val quota = viewModel.memoryQuota
|
||||
val memoryList = viewModel.memoryList
|
||||
val isLoading = viewModel.isLoadingMemory
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.background(Color(0xFFFAF9FB))
|
||||
) {
|
||||
// 顶部栏:返回按钮 + 标题 + 加号按钮
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(44.dp)
|
||||
.padding(horizontal = 16.dp)
|
||||
) {
|
||||
// 中间标题 - 绝对居中,不受其他组件影响
|
||||
Text(
|
||||
text = stringResource(R.string.group_chat_info_memory_manage),
|
||||
style = TextStyle(
|
||||
color = Color.Black,
|
||||
fontSize = 17.sp,
|
||||
fontWeight = FontWeight.SemiBold
|
||||
),
|
||||
modifier = Modifier.align(Alignment.Center),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
|
||||
// 左侧返回按钮
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterStart)
|
||||
.clip(RoundedCornerShape(296.dp))
|
||||
.background(Color.White)
|
||||
.noRippleClickable { onDismiss() }
|
||||
.padding(horizontal = 12.dp, vertical = 8.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.rider_pro_back_icon),
|
||||
contentDescription = "返回",
|
||||
modifier = Modifier.size(16.dp),
|
||||
colorFilter = ColorFilter.tint(Color.Black)
|
||||
)
|
||||
Text(
|
||||
text = "返回",
|
||||
style = TextStyle(
|
||||
color = Color.Black,
|
||||
fontSize = 15.sp,
|
||||
fontWeight = FontWeight.Normal
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// 右侧圆形加号按钮
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.CenterEnd)
|
||||
.size(32.dp)
|
||||
.clip(CircleShape)
|
||||
.background(Color.White)
|
||||
.noRippleClickable { onAddMemoryClick() },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = "+",
|
||||
style = TextStyle(
|
||||
color = Color.Black,
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 浅黄色提示栏 - 显示真实的配额数据
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.background(Color(0xFFFBF8EF))
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(16.dp), verticalAlignment = Alignment.CenterVertically) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text("已付费:", style = TextStyle(color = Color(0x993C3C43), fontSize = 13.sp))
|
||||
Spacer(Modifier.width(3.dp))
|
||||
Text(
|
||||
"${quota?.purchasedCount ?: 0}",
|
||||
style = TextStyle(color = Color(0xFFFF8D28), fontSize = 13.sp)
|
||||
)
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text("已使用:", style = TextStyle(color = Color(0x993C3C43), fontSize = 13.sp))
|
||||
Spacer(Modifier.width(3.dp))
|
||||
Text(
|
||||
"${quota?.currentCount ?: 0}",
|
||||
style = TextStyle(color = Color(0xFFFF8D28), fontSize = 13.sp)
|
||||
)
|
||||
}
|
||||
}
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text("可用上限:", style = TextStyle(color = Color(0x993C3C43), fontSize = 13.sp))
|
||||
Spacer(Modifier.width(3.dp))
|
||||
Text(
|
||||
"${quota?.totalMaxCount ?: 50}",
|
||||
style = TextStyle(color = Color(0xFFFF8D28), fontSize = 13.sp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 记忆列表或空状态
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
) {
|
||||
if (isLoading) {
|
||||
// 加载中状态
|
||||
Box(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(40.dp),
|
||||
color = Color(0xFFFF8D28)
|
||||
)
|
||||
}
|
||||
} else if (memoryList.isNotEmpty()) {
|
||||
// 显示记忆列表
|
||||
LazyColumn(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentPadding = PaddingValues(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
items(memoryList) { memory ->
|
||||
AgentMemoryItem(
|
||||
memory = memory,
|
||||
isEditing = editingMemoryId == memory.id,
|
||||
onEdit = {
|
||||
editingMemoryId = memory.id
|
||||
},
|
||||
onCancel = {
|
||||
editingMemoryId = null
|
||||
},
|
||||
onSave = { newText ->
|
||||
viewModel.updateMemory(memory.id, newText)
|
||||
editingMemoryId = null
|
||||
},
|
||||
onDelete = {
|
||||
viewModel.deleteMemory(memory.id)
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(vertical = 60.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.mipmap.group),
|
||||
contentDescription = "暂无记忆",
|
||||
modifier = Modifier
|
||||
.height(150.dp).width(180.dp)
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(10.dp))
|
||||
Text(
|
||||
text = "暂无记忆",
|
||||
style = TextStyle(color = Color.Black, fontSize = 16.sp, fontWeight = FontWeight.SemiBold)
|
||||
)
|
||||
Spacer(Modifier.height(6.dp))
|
||||
Text(
|
||||
text = "点击上方按钮添加 Agent 记忆",
|
||||
style = TextStyle(color = Color.Black, fontSize = 14.sp, fontWeight = FontWeight.Normal)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Agent 记忆项组件(不显示创建者信息)
|
||||
*/
|
||||
@Composable
|
||||
fun AgentMemoryItem(
|
||||
memory: AgentRuleEntity,
|
||||
isEditing: Boolean = false,
|
||||
onEdit: () -> Unit = {},
|
||||
onCancel: () -> Unit = {},
|
||||
onSave: (String) -> Unit = {},
|
||||
onDelete: () -> Unit
|
||||
) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
val context = LocalContext.current
|
||||
var memoryText by remember { mutableStateOf(memory.rule) }
|
||||
val maxLength = 500
|
||||
|
||||
// 渐变边框颜色
|
||||
val gradientColors = listOf(
|
||||
Color(0xFF7C45ED),
|
||||
Color(0xFF7C57EE),
|
||||
Color(0xFF7BD8F8)
|
||||
)
|
||||
val gradientBrush = Brush.horizontalGradient(colors = gradientColors)
|
||||
|
||||
// 当进入编辑模式时,重置文本
|
||||
LaunchedEffect(isEditing) {
|
||||
if (isEditing) {
|
||||
memoryText = memory.rule
|
||||
}
|
||||
}
|
||||
|
||||
if (isEditing) {
|
||||
// 编辑模式:显示编辑界面
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(Color.White)
|
||||
.padding(horizontal = 12.dp, vertical = 16.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
// 文本输入框 - 带渐变边框
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(88.dp)
|
||||
) {
|
||||
// 渐变边框层
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(brush = gradientBrush)
|
||||
)
|
||||
|
||||
// 内容层 - 白色背景,通过padding形成边框效果
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(1.dp)
|
||||
.clip(RoundedCornerShape(15.dp))
|
||||
.background(Color.White)
|
||||
.padding(12.dp),
|
||||
contentAlignment = Alignment.TopStart
|
||||
) {
|
||||
BasicTextField(
|
||||
value = memoryText,
|
||||
onValueChange = { newText ->
|
||||
if (newText.length <= maxLength) {
|
||||
memoryText = newText
|
||||
}
|
||||
},
|
||||
cursorBrush = SolidColor(Color.Black),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
textStyle = TextStyle(
|
||||
fontSize = 13.sp,
|
||||
color = Color.Black,
|
||||
lineHeight = 18.sp
|
||||
),
|
||||
decorationBox = { innerTextField ->
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.TopStart
|
||||
) {
|
||||
innerTextField()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 按钮行:取消和保存
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
// 取消按钮
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.height(34.dp)
|
||||
.clip(RoundedCornerShape(653.8.dp))
|
||||
.background(Color(0x147C7480))
|
||||
.noRippleClickable { onCancel() },
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = "取消",
|
||||
style = TextStyle(
|
||||
fontSize = 15.sp,
|
||||
color = Color.Black
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// 保存按钮
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.height(34.dp)
|
||||
.clip(RoundedCornerShape(653.8.dp))
|
||||
.background(Color(0xFF110C13))
|
||||
.noRippleClickable {
|
||||
if (memoryText.isNotBlank() && memoryText != memory.rule) {
|
||||
onSave(memoryText)
|
||||
Toast.makeText(context, "记忆更新成功", Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = "保存",
|
||||
style = TextStyle(
|
||||
fontSize = 15.sp,
|
||||
color = Color.White
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 显示模式:显示记忆内容(不显示创建者信息)
|
||||
// 格式化日期:从 "2025-10-20T10:30:00Z" 格式转换为 "2025年10月20日"
|
||||
val formattedDate = try {
|
||||
if (memory.createdAt.length >= 10) {
|
||||
val dateStr = memory.createdAt.substring(0, 10)
|
||||
val parts = dateStr.split("-")
|
||||
if (parts.size == 3) {
|
||||
"${parts[0]}年${parts[1].toInt()}月${parts[2].toInt()}日"
|
||||
} else {
|
||||
dateStr
|
||||
}
|
||||
} else {
|
||||
memory.createdAt
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
if (memory.createdAt.length >= 10) memory.createdAt.substring(0, 10) else memory.createdAt
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(Color.White)
|
||||
.padding(horizontal = 12.dp, vertical = 16.dp)
|
||||
) {
|
||||
// 主文本 - 顶部
|
||||
Text(
|
||||
text = memory.rule,
|
||||
style = TextStyle(
|
||||
color = Color.Black,
|
||||
fontSize = 13.sp,
|
||||
lineHeight = 18.sp
|
||||
),
|
||||
maxLines = 3,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// 底部行:时间 + 编辑删除按钮(不显示创建者信息)
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// 时间信息 - 左侧
|
||||
Text(
|
||||
text = formattedDate,
|
||||
style = TextStyle(
|
||||
color = Color(0x993C3C43),
|
||||
fontSize = 11.sp
|
||||
)
|
||||
)
|
||||
|
||||
// 编辑和删除图标 - 右侧
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
// 编辑图标
|
||||
Image(
|
||||
painter = painterResource(R.mipmap.icons_infor_edit),
|
||||
contentDescription = "编辑",
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.noRippleClickable { onEdit() },
|
||||
colorFilter = ColorFilter.tint(Color.Black)
|
||||
)
|
||||
|
||||
Image(
|
||||
painter = painterResource(R.mipmap.iconsdelete),
|
||||
contentDescription = "删除",
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.noRippleClickable { onDelete() },
|
||||
colorFilter = ColorFilter.tint(Color(0xFFEE2A33))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 Agent 记忆对话框
|
||||
*/
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AddAgentMemoryDialog(
|
||||
profile: AccountProfileEntity?,
|
||||
viewModel: AgentMemoryManageViewModel,
|
||||
onDismiss: () -> Unit,
|
||||
onRequestAddMemory: (String) -> Unit // 改为请求添加,而不是直接添加
|
||||
) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
val context = LocalContext.current
|
||||
var memoryText by remember { mutableStateOf("") }
|
||||
val maxLength = 500
|
||||
|
||||
// 监听添加记忆的结果
|
||||
LaunchedEffect(viewModel.addMemorySuccess) {
|
||||
if (viewModel.addMemorySuccess) {
|
||||
Toast.makeText(context, context.getString(R.string.group_chat_info_memory_add_success), Toast.LENGTH_SHORT).show()
|
||||
memoryText = "" // 清空输入框
|
||||
onDismiss()
|
||||
viewModel.addMemorySuccess = false
|
||||
}
|
||||
}
|
||||
|
||||
LaunchedEffect(viewModel.addMemoryError) {
|
||||
viewModel.addMemoryError?.let { error ->
|
||||
Toast.makeText(context, error, Toast.LENGTH_SHORT).show()
|
||||
viewModel.addMemoryError = null
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(bottom = 40.dp),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
// 顶部标题栏
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(44.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
Spacer(modifier = Modifier.width(24.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.group_chat_info_add_memory),
|
||||
style = TextStyle(
|
||||
fontSize = 17.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = Color.Black
|
||||
),
|
||||
modifier = Modifier.weight(1f),
|
||||
textAlign = TextAlign.Center
|
||||
)
|
||||
Image(
|
||||
painter = painterResource(R.drawable.rider_pro_close),
|
||||
contentDescription = "关闭",
|
||||
modifier = Modifier
|
||||
.size(24.dp)
|
||||
.noRippleClickable { onDismiss() },
|
||||
colorFilter = ColorFilter.tint(Color.Black)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
Column(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.spacedBy(16.dp)
|
||||
) {
|
||||
// Agent 信息卡片
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(80.dp)
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(Color.White)
|
||||
.padding(horizontal = 12.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
if (profile?.avatar?.isNotEmpty() == true) {
|
||||
CustomAsyncImage(
|
||||
imageUrl = profile.avatar,
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(RoundedCornerShape(12.dp)),
|
||||
contentDescription = "Agent 头像",
|
||||
context = LocalContext.current
|
||||
)
|
||||
} else {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(48.dp)
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(AppColors.decentBackground),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = profile?.nickName?.firstOrNull()?.toString() ?: "",
|
||||
style = TextStyle(
|
||||
fontSize = 20.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = AppColors.text
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
modifier = Modifier.weight(1f),
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Text(
|
||||
text = profile?.nickName ?: "",
|
||||
style = TextStyle(
|
||||
fontSize = 15.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
color = Color.Black
|
||||
)
|
||||
)
|
||||
if (!profile?.bio.isNullOrEmpty()) {
|
||||
Text(
|
||||
text = profile?.bio ?: "",
|
||||
style = TextStyle(
|
||||
fontSize = 12.sp,
|
||||
color = Color(0xFF3C3C43).copy(alpha = 0.6f)
|
||||
),
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 输入框卡片
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(Color.White)
|
||||
) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(12.dp)
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(84.dp),
|
||||
contentAlignment = Alignment.TopStart
|
||||
) {
|
||||
BasicTextField(
|
||||
value = memoryText,
|
||||
onValueChange = { newText ->
|
||||
if (newText.length <= maxLength) {
|
||||
memoryText = newText
|
||||
}
|
||||
},
|
||||
cursorBrush = SolidColor(Color.Black),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
maxLines = 6,
|
||||
textStyle = TextStyle(
|
||||
fontSize = 15.sp,
|
||||
color = Color.Black,
|
||||
lineHeight = 20.sp
|
||||
),
|
||||
decorationBox = { innerTextField ->
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.TopStart
|
||||
) {
|
||||
innerTextField()
|
||||
if (memoryText.isEmpty()) {
|
||||
Text(
|
||||
text = stringResource(R.string.group_chat_info_memory_input_hint),
|
||||
style = TextStyle(
|
||||
fontSize = 15.sp,
|
||||
color = Color.Black.copy(alpha = 0.3f)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
Text(
|
||||
text = "${memoryText.length}/$maxLength",
|
||||
style = TextStyle(
|
||||
fontSize = 12.sp,
|
||||
color = Color(0xFF3C3C43).copy(alpha = 0.3f)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 提示信息卡片
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(Color(0xFFFBF8EF))
|
||||
.padding(horizontal = 16.dp, vertical = 12.dp),
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "⭐",
|
||||
fontSize = 13.sp
|
||||
)
|
||||
val memoryCost = viewModel.addMemoryCost
|
||||
Text(
|
||||
text = if (memoryCost != null && memoryCost > 0) {
|
||||
"添加记忆需消耗 ${memoryCost}派币"
|
||||
} else {
|
||||
stringResource(R.string.group_chat_info_memory_cost)
|
||||
},
|
||||
style = TextStyle(
|
||||
fontSize = 13.sp,
|
||||
color = Color.Black
|
||||
)
|
||||
)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "🤖",
|
||||
fontSize = 13.sp
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.group_chat_info_memory_optimization),
|
||||
style = TextStyle(
|
||||
fontSize = 13.sp,
|
||||
color = Color.Black
|
||||
)
|
||||
)
|
||||
}
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
text = "✏️",
|
||||
fontSize = 13.sp
|
||||
)
|
||||
Text(
|
||||
text = stringResource(R.string.group_chat_info_memory_editable),
|
||||
style = TextStyle(
|
||||
fontSize = 13.sp,
|
||||
color = Color.Black
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加记忆按钮
|
||||
val isButtonEnabled = memoryText.isNotEmpty() && !viewModel.isAddingMemory
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(50.dp)
|
||||
.clip(RoundedCornerShape(1000.dp))
|
||||
.then(
|
||||
if (isButtonEnabled) {
|
||||
Modifier.background(
|
||||
brush = Brush.horizontalGradient(
|
||||
colors = listOf(
|
||||
Color(0xFF7C45ED),
|
||||
Color(0x997C57EE),
|
||||
Color(0x887BD8F8)
|
||||
)
|
||||
)
|
||||
)
|
||||
} else {
|
||||
Modifier.background(
|
||||
brush = Brush.horizontalGradient(
|
||||
colors = listOf(
|
||||
Color(0x337C45ED),
|
||||
Color(0x337C57EE),
|
||||
Color(0x337BD8F8)
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
)
|
||||
.noRippleClickable {
|
||||
if (isButtonEnabled) {
|
||||
onRequestAddMemory(memoryText)
|
||||
}
|
||||
},
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
if (viewModel.isAddingMemory) {
|
||||
CircularProgressIndicator(
|
||||
modifier = Modifier.size(20.dp),
|
||||
color = Color.White
|
||||
)
|
||||
} else {
|
||||
Text(
|
||||
text = stringResource(R.string.group_chat_info_add_memory),
|
||||
style = TextStyle(
|
||||
fontSize = 17.sp,
|
||||
color = if (isButtonEnabled) Color.White else Color.White.copy(alpha = 0.6f)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,207 @@
|
||||
package com.aiosman.ravenow.ui.profile
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.lifecycle.ViewModel
|
||||
import androidx.lifecycle.viewModelScope
|
||||
import com.aiosman.ravenow.data.AgentRuleService
|
||||
import com.aiosman.ravenow.data.AgentRuleServiceImpl
|
||||
import com.aiosman.ravenow.data.AgentRuleEntity
|
||||
import com.aiosman.ravenow.data.AgentRuleQuotaEntity
|
||||
import com.aiosman.ravenow.data.PointService
|
||||
import kotlinx.coroutines.flow.first
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
class AgentMemoryManageViewModel(
|
||||
private val openId: String
|
||||
) : ViewModel() {
|
||||
|
||||
// 记忆管理相关状态
|
||||
var memoryQuota by mutableStateOf<AgentRuleQuotaEntity?>(null)
|
||||
var memoryList by mutableStateOf<List<AgentRuleEntity>>(emptyList())
|
||||
var isLoadingMemory by mutableStateOf(false)
|
||||
var memoryError by mutableStateOf<String?>(null)
|
||||
|
||||
// Agent 规则服务
|
||||
private val agentRuleService: AgentRuleService = AgentRuleServiceImpl()
|
||||
|
||||
// 添加记忆相关状态
|
||||
var isAddingMemory by mutableStateOf(false)
|
||||
var addMemoryError by mutableStateOf<String?>(null)
|
||||
var addMemorySuccess by mutableStateOf(false)
|
||||
var addMemoryCost by mutableStateOf<Int?>(null)
|
||||
var pointsBalance by mutableStateOf<Int?>(null)
|
||||
|
||||
init {
|
||||
loadMemoryCost()
|
||||
loadPointsBalance()
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载积分余额
|
||||
*/
|
||||
fun loadPointsBalance() {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
PointService.refreshMyPointsBalance(includeStatistics = false)
|
||||
val balance = PointService.pointsBalance.first()
|
||||
pointsBalance = balance?.balance
|
||||
} catch (e: Exception) {
|
||||
Log.e("AgentMemoryManageViewModel", "加载积分余额失败: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 添加 Agent 记忆
|
||||
* @param memoryText 记忆内容
|
||||
*/
|
||||
fun addAgentMemory(memoryText: String) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
isAddingMemory = true
|
||||
addMemoryError = null
|
||||
addMemorySuccess = false
|
||||
|
||||
// 使用 Agent 规则接口创建记忆
|
||||
agentRuleService.createAgentRuleByOpenId(
|
||||
openId = openId,
|
||||
rule = memoryText
|
||||
)
|
||||
|
||||
addMemorySuccess = true
|
||||
Log.d("AgentMemoryManageViewModel", "Agent 记忆添加成功")
|
||||
// 刷新记忆列表和配额
|
||||
loadMemoryQuota()
|
||||
loadMemoryList()
|
||||
} catch (e: Exception) {
|
||||
addMemoryError = e.message ?: "添加 Agent 记忆失败"
|
||||
Log.e("AgentMemoryManageViewModel", "添加 Agent 记忆失败: ${e.message}", e)
|
||||
} finally {
|
||||
isAddingMemory = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取记忆配额信息(Agent 规则配额)
|
||||
*/
|
||||
fun loadMemoryQuota() {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
isLoadingMemory = true
|
||||
memoryError = null
|
||||
|
||||
// 使用 Agent 规则接口获取配额
|
||||
memoryQuota = agentRuleService.getAgentRuleQuota(openId = openId)
|
||||
} catch (e: Exception) {
|
||||
memoryError = e.message ?: "获取配额信息失败"
|
||||
Log.e("AgentMemoryManageViewModel", "获取配额信息失败: ${e.message}", e)
|
||||
} finally {
|
||||
isLoadingMemory = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 获取记忆列表(Agent 规则列表)
|
||||
*/
|
||||
fun loadMemoryList(page: Int = 1, pageSize: Int = 20) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
isLoadingMemory = true
|
||||
memoryError = null
|
||||
|
||||
// 使用 Agent 规则接口获取列表
|
||||
val result = agentRuleService.getAgentRuleList(
|
||||
openId = openId,
|
||||
keyword = null,
|
||||
page = page,
|
||||
pageSize = pageSize
|
||||
)
|
||||
memoryList = result.list
|
||||
} catch (e: Exception) {
|
||||
memoryError = e.message ?: "获取记忆列表失败"
|
||||
Log.e("AgentMemoryManageViewModel", "获取记忆列表失败: ${e.message}", e)
|
||||
} finally {
|
||||
isLoadingMemory = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 删除记忆(Agent 规则)
|
||||
*/
|
||||
fun deleteMemory(ruleId: Int) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
isLoadingMemory = true
|
||||
memoryError = null
|
||||
|
||||
// 使用 Agent 规则接口删除
|
||||
agentRuleService.deleteAgentRule(ruleId)
|
||||
|
||||
// 刷新记忆列表和配额
|
||||
loadMemoryQuota()
|
||||
loadMemoryList()
|
||||
} catch (e: Exception) {
|
||||
memoryError = e.message ?: "删除记忆失败"
|
||||
Log.e("AgentMemoryManageViewModel", "删除记忆失败: ${e.message}", e)
|
||||
} finally {
|
||||
isLoadingMemory = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 更新记忆(Agent 规则)
|
||||
*/
|
||||
fun updateMemory(ruleId: Int, newRuleText: String) {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
isLoadingMemory = true
|
||||
memoryError = null
|
||||
|
||||
// 使用 Agent 规则接口更新
|
||||
agentRuleService.updateAgentRule(
|
||||
id = ruleId,
|
||||
rule = newRuleText,
|
||||
openId = openId
|
||||
)
|
||||
|
||||
// 刷新记忆列表和配额
|
||||
loadMemoryQuota()
|
||||
loadMemoryList()
|
||||
} catch (e: Exception) {
|
||||
memoryError = e.message ?: "更新记忆失败"
|
||||
Log.e("AgentMemoryManageViewModel", "更新记忆失败: ${e.message}", e)
|
||||
} finally {
|
||||
isLoadingMemory = false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 加载添加 Agent 记忆的价格
|
||||
*/
|
||||
fun loadMemoryCost() {
|
||||
viewModelScope.launch {
|
||||
try {
|
||||
// 获取积分规则中的 Agent 记忆价格
|
||||
PointService.refreshPointsRules()
|
||||
val rules = PointService.pointsRules.first()
|
||||
val agentMemoryRule = rules?.sub?.get(PointService.PointsRuleKey.ADD_AGENT_MEMORY)
|
||||
addMemoryCost = when (agentMemoryRule) {
|
||||
is PointService.RuleAmount.Fixed -> agentMemoryRule.value
|
||||
is PointService.RuleAmount.Range -> agentMemoryRule.min
|
||||
else -> null
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
Log.e("AgentMemoryManageViewModel", "加载记忆价格失败: ${e.message}", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -57,6 +57,7 @@ fun AiProfileV3(
|
||||
onFollowClick: () -> Unit = {},
|
||||
onChatClick: () -> Unit = {},
|
||||
onShareClick: () -> Unit = {},
|
||||
onMemoryManageClick: () -> Unit = {},
|
||||
onLoadMore: () -> Unit = {},
|
||||
onComment: (MomentEntity) -> Unit = {},
|
||||
) {
|
||||
@@ -181,6 +182,10 @@ fun AiProfileV3(
|
||||
)
|
||||
}
|
||||
},
|
||||
onMemoryManageClick = {
|
||||
showMenu = false
|
||||
onMemoryManageClick()
|
||||
},
|
||||
profile = profile,
|
||||
showEdit = isCreator
|
||||
)
|
||||
@@ -525,6 +530,7 @@ private fun AiProfileMenuModal(
|
||||
onChatClick: () -> Unit,
|
||||
onShareClick: () -> Unit,
|
||||
onEditClick: () -> Unit,
|
||||
onMemoryManageClick: () -> Unit,
|
||||
showEdit: Boolean = false,
|
||||
profile: AccountProfileEntity? = null
|
||||
) {
|
||||
@@ -541,7 +547,7 @@ private fun AiProfileMenuModal(
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(if (showEdit) 240.dp else 160.dp)
|
||||
.height(if (showEdit) 240.dp else 240.dp)
|
||||
.background(appColors.background)
|
||||
.padding(vertical = 47.dp, horizontal = 20.dp)
|
||||
) {
|
||||
@@ -581,7 +587,7 @@ private fun AiProfileMenuModal(
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(end = if (showEdit) 16.dp else 0.dp),
|
||||
.padding(end = 16.dp),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
@@ -609,6 +615,38 @@ private fun AiProfileMenuModal(
|
||||
)
|
||||
}
|
||||
|
||||
// 记忆管理选项
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.padding(end = if (showEdit) 16.dp else 0.dp),
|
||||
verticalArrangement = Arrangement.Center,
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clip(CircleShape)
|
||||
.noRippleClickable {
|
||||
onMemoryManageClick()
|
||||
}
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(id = R.drawable.ic_brain_add),
|
||||
contentDescription = "",
|
||||
modifier = Modifier.size(24.dp),
|
||||
colorFilter = ColorFilter.tint(appColors.text)
|
||||
)
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = stringResource(R.string.group_chat_info_memory_manage),
|
||||
fontSize = 12.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
color = appColors.text
|
||||
)
|
||||
}
|
||||
|
||||
// 编辑选项(仅创建者可见)
|
||||
if (showEdit) {
|
||||
Column(
|
||||
|
||||
@@ -1,22 +1,44 @@
|
||||
package com.aiosman.ravenow.ui.profile
|
||||
|
||||
import android.util.Log
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.Box
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.ExperimentalMaterial3Api
|
||||
import androidx.compose.material3.ModalBottomSheet
|
||||
import androidx.compose.material3.rememberModalBottomSheetState
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.LaunchedEffect
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.runtime.rememberCoroutineScope
|
||||
import androidx.compose.runtime.setValue
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.lifecycle.viewmodel.compose.viewModel
|
||||
import com.aiosman.ravenow.GuestLoginCheckOut
|
||||
import com.aiosman.ravenow.GuestLoginCheckOutScene
|
||||
import com.aiosman.ravenow.LocalNavController
|
||||
import com.aiosman.ravenow.R
|
||||
import com.aiosman.ravenow.data.UserServiceImpl
|
||||
import com.aiosman.ravenow.exp.viewModelFactory
|
||||
import com.aiosman.ravenow.ui.NavigationRoute
|
||||
import com.aiosman.ravenow.ui.composables.PointsPaymentDialog
|
||||
import com.aiosman.ravenow.ui.index.tabs.ai.tabs.mine.MineAgentViewModel
|
||||
import com.aiosman.ravenow.ui.index.tabs.profile.MyProfileViewModel
|
||||
import com.aiosman.ravenow.ui.navigateToChatAi
|
||||
import com.aiosman.ravenow.ui.navigateToPost
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterial3Api::class)
|
||||
@Composable
|
||||
fun AiProfileWrap(id: String) {
|
||||
val model: AiProfileViewModel = viewModel(factory = viewModelFactory {
|
||||
@@ -26,6 +48,26 @@ fun AiProfileWrap(id: String) {
|
||||
val navController = LocalNavController.current
|
||||
val scope = rememberCoroutineScope()
|
||||
|
||||
// 记忆管理相关状态
|
||||
var showMemoryManageDialog by remember { mutableStateOf(false) }
|
||||
var showAddMemoryDialog by remember { mutableStateOf(false) }
|
||||
var showAddMemoryConfirmDialog by remember { mutableStateOf(false) }
|
||||
var pendingMemoryText by remember { mutableStateOf("") }
|
||||
val memoryManageSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val addMemorySheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
|
||||
// 获取 chatAIId (openId)
|
||||
val chatAIId = model.profile?.chatAIId ?: ""
|
||||
|
||||
// 创建记忆管理 ViewModel
|
||||
val memoryViewModel: AgentMemoryManageViewModel? = if (chatAIId.isNotEmpty()) {
|
||||
viewModel(factory = viewModelFactory {
|
||||
AgentMemoryManageViewModel(chatAIId)
|
||||
}, key = "agentMemoryViewModel_${chatAIId}")
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
model.loadProfile(id)
|
||||
MyProfileViewModel.loadProfile()
|
||||
@@ -33,6 +75,7 @@ fun AiProfileWrap(id: String) {
|
||||
|
||||
val isSelf = id == MyProfileViewModel.profile?.id.toString()
|
||||
|
||||
Box(modifier = Modifier.fillMaxSize()) {
|
||||
AiProfileV3(
|
||||
profile = model.profile,
|
||||
moments = model.moments,
|
||||
@@ -78,6 +121,11 @@ fun AiProfileWrap(id: String) {
|
||||
// TODO: 实现分享逻辑
|
||||
Log.d("AiProfileWrap", "分享功能待实现")
|
||||
},
|
||||
onMemoryManageClick = {
|
||||
if (chatAIId.isNotEmpty()) {
|
||||
showMemoryManageDialog = true
|
||||
}
|
||||
},
|
||||
onLoadMore = {
|
||||
Log.d("AiProfileWrap", "onLoadMore被调用")
|
||||
model.loadMoreMoment()
|
||||
@@ -86,5 +134,115 @@ fun AiProfileWrap(id: String) {
|
||||
navController.navigateToPost(moment.id)
|
||||
}
|
||||
)
|
||||
|
||||
// 添加记忆对话框
|
||||
if (showAddMemoryDialog && memoryViewModel != null) {
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = { showAddMemoryDialog = false },
|
||||
sheetState = addMemorySheetState,
|
||||
containerColor = Color(0xFFFAF9FB),
|
||||
dragHandle = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(36.dp)
|
||||
.height(5.dp)
|
||||
.padding(top = 5.dp)
|
||||
.background(
|
||||
Color(0xFFCCCCCC),
|
||||
RoundedCornerShape(100.dp)
|
||||
)
|
||||
)
|
||||
},
|
||||
shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp)
|
||||
) {
|
||||
AddAgentMemoryDialog(
|
||||
profile = model.profile,
|
||||
viewModel = memoryViewModel,
|
||||
onDismiss = { showAddMemoryDialog = false },
|
||||
onRequestAddMemory = { memoryText ->
|
||||
// 关闭添加对话框,显示确认框
|
||||
showAddMemoryDialog = false
|
||||
pendingMemoryText = memoryText
|
||||
showAddMemoryConfirmDialog = true
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 记忆管理弹窗
|
||||
if (showMemoryManageDialog && memoryViewModel != null && chatAIId.isNotEmpty()) {
|
||||
ModalBottomSheet(
|
||||
onDismissRequest = { showMemoryManageDialog = false },
|
||||
sheetState = memoryManageSheetState,
|
||||
containerColor = Color(0xFFFAF9FB),
|
||||
dragHandle = {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.width(36.dp)
|
||||
.height(5.dp)
|
||||
.padding(top = 5.dp)
|
||||
.background(
|
||||
Color(0xFFCCCCCC),
|
||||
RoundedCornerShape(100.dp)
|
||||
)
|
||||
)
|
||||
},
|
||||
shape = RoundedCornerShape(topStart = 20.dp, topEnd = 20.dp)
|
||||
) {
|
||||
// 立即展开到全屏,避免逐渐变高的动画
|
||||
LaunchedEffect(Unit) {
|
||||
memoryManageSheetState.expand()
|
||||
}
|
||||
AgentMemoryManageContent(
|
||||
openId = chatAIId,
|
||||
profile = model.profile,
|
||||
viewModel = memoryViewModel,
|
||||
onAddMemoryClick = {
|
||||
showMemoryManageDialog = false
|
||||
showAddMemoryDialog = true
|
||||
},
|
||||
onDismiss = {
|
||||
showMemoryManageDialog = false
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
// 添加记忆确认对话框
|
||||
if (showAddMemoryConfirmDialog && memoryViewModel != null) {
|
||||
val cost = memoryViewModel.addMemoryCost ?: 0
|
||||
val currentBalance = memoryViewModel.pointsBalance ?: 0
|
||||
val balanceAfterCost = (currentBalance - cost).coerceAtLeast(0)
|
||||
val isBalanceSufficient = currentBalance >= cost
|
||||
|
||||
// 监听添加成功,关闭确认对话框并刷新积分余额
|
||||
LaunchedEffect(memoryViewModel.addMemorySuccess) {
|
||||
if (memoryViewModel.addMemorySuccess) {
|
||||
showAddMemoryConfirmDialog = false
|
||||
pendingMemoryText = ""
|
||||
memoryViewModel.addMemorySuccess = false
|
||||
memoryViewModel.loadPointsBalance() // 刷新积分余额
|
||||
}
|
||||
}
|
||||
|
||||
PointsPaymentDialog(
|
||||
cost = cost,
|
||||
currentBalance = currentBalance,
|
||||
balanceAfterCost = balanceAfterCost,
|
||||
isBalanceSufficient = isBalanceSufficient,
|
||||
onConfirm = {
|
||||
// 确认支付,添加记忆
|
||||
memoryViewModel.addAgentMemory(pendingMemoryText)
|
||||
},
|
||||
onCancel = {
|
||||
showAddMemoryConfirmDialog = false
|
||||
pendingMemoryText = ""
|
||||
},
|
||||
title = stringResource(R.string.group_chat_info_add_memory),
|
||||
description = stringResource(R.string.group_chat_info_memory_description),
|
||||
isProcessing = memoryViewModel.isAddingMemory
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user