群记忆项编辑功能;群权限设置UI;群记忆管理缺省图

This commit is contained in:
2025-11-05 21:29:18 +08:00
parent 4a1c15747c
commit 9ea03cee34
12 changed files with 792 additions and 119 deletions

View File

@@ -63,6 +63,7 @@ fun GroupChatInfoScreen(groupId: String) {
var showAddMemoryDialog by remember { mutableStateOf(false) } var showAddMemoryDialog by remember { mutableStateOf(false) }
var showMemoryManageDialog by remember { mutableStateOf(false) } var showMemoryManageDialog by remember { mutableStateOf(false) }
var showVisibilityDialog by remember { mutableStateOf(false) }
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
val memoryManageSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true) val memoryManageSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
@@ -150,7 +151,7 @@ fun GroupChatInfoScreen(groupId: String) {
Spacer(modifier = Modifier.height(8.dp)) Spacer(modifier = Modifier.height(8.dp))
Text( Text(
text = viewModel.groupInfo?.groupName ?: "", text = viewModel.groupInfo?.groupName ?: stringResource(R.string.group_info_edit),
style = androidx.compose.ui.text.TextStyle( style = androidx.compose.ui.text.TextStyle(
color = AppColors.text, color = AppColors.text,
fontSize = 16.sp, fontSize = 16.sp,
@@ -167,7 +168,7 @@ fun GroupChatInfoScreen(groupId: String) {
) )
Spacer(modifier = Modifier.width(4.dp)) Spacer(modifier = Modifier.width(4.dp))
Text( Text(
text = "4", text = "${viewModel.groupInfo?.memberCount ?: 0}",
style = androidx.compose.ui.text.TextStyle( style = androidx.compose.ui.text.TextStyle(
color = AppColors.text.copy(alpha = 0.7f), color = AppColors.text.copy(alpha = 0.7f),
fontSize = 11.sp fontSize = 11.sp
@@ -435,6 +436,7 @@ fun GroupChatInfoScreen(groupId: String) {
.clip(RoundedCornerShape(8.dp)) .clip(RoundedCornerShape(8.dp))
.padding(12.dp) .padding(12.dp)
.noRippleClickable { .noRippleClickable {
showVisibilityDialog = true
}, },
verticalAlignment = Alignment.CenterVertically verticalAlignment = Alignment.CenterVertically
) { ) {
@@ -566,6 +568,33 @@ fun GroupChatInfoScreen(groupId: String) {
} }
} }
// 群可见性弹窗
if (showVisibilityDialog) {
val visibilitySheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
ModalBottomSheet(
onDismissRequest = { showVisibilityDialog = false },
sheetState = visibilitySheetState,
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)
) {
GroupVisibilityDialog(
onDismiss = { showVisibilityDialog = false }
)
}
}
// 添加群记忆弹窗 // 添加群记忆弹窗
if (showAddMemoryDialog) { if (showAddMemoryDialog) {
ModalBottomSheet( ModalBottomSheet(
@@ -623,6 +652,9 @@ fun GroupChatInfoScreen(groupId: String) {
onAddMemoryClick = { onAddMemoryClick = {
showMemoryManageDialog = false showMemoryManageDialog = false
showAddMemoryDialog = true showAddMemoryDialog = true
},
onDismiss = {
showMemoryManageDialog = false
} }
) )
} }
@@ -645,7 +677,7 @@ fun AddGroupMemoryDialog(
// 监听添加记忆的结果 // 监听添加记忆的结果
LaunchedEffect(viewModel.addMemorySuccess) { LaunchedEffect(viewModel.addMemorySuccess) {
if (viewModel.addMemorySuccess) { if (viewModel.addMemorySuccess) {
android.widget.Toast.makeText(context, "群记忆添加成功", android.widget.Toast.LENGTH_SHORT).show() android.widget.Toast.makeText(context, context.getString(R.string.group_chat_info_memory_add_success), android.widget.Toast.LENGTH_SHORT).show()
memoryText = "" // 清空输入框 memoryText = "" // 清空输入框
onDismiss() onDismiss()
viewModel.addMemorySuccess = false viewModel.addMemorySuccess = false
@@ -745,7 +777,7 @@ fun AddGroupMemoryDialog(
verticalArrangement = Arrangement.spacedBy(4.dp) verticalArrangement = Arrangement.spacedBy(4.dp)
) { ) {
Text( Text(
text = groupInfo?.groupName ?: "创意项目讨论组", text = groupInfo?.groupName ?: "",
style = TextStyle( style = TextStyle(
fontSize = 15.sp, fontSize = 15.sp,
fontWeight = FontWeight.SemiBold, fontWeight = FontWeight.SemiBold,
@@ -889,22 +921,37 @@ fun AddGroupMemoryDialog(
} }
// 添加记忆按钮 // 添加记忆按钮
val isButtonEnabled = memoryText.isNotEmpty() && !viewModel.isAddingMemory
Box( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(50.dp) .height(50.dp)
.clip(RoundedCornerShape(1000.dp)) .clip(RoundedCornerShape(1000.dp))
.background( .then(
if (isButtonEnabled) {
Modifier.background(
brush = Brush.horizontalGradient( brush = Brush.horizontalGradient(
colors = listOf( colors = listOf(
Color(0xFF7C45ED), // 紫色 Color(0xFF7C45ED),
Color(0xFF7C57EE), // 浅紫色 Color(0x997C57EE),
Color(0xFF7BD8F8) // 蓝色 Color(0x887BD8F8)
) )
) )
) )
} else {
Modifier.background(
brush = Brush.horizontalGradient(
colors = listOf(
Color(0x337C45ED),
Color(0x337C57EE),
Color(0x337BD8F8)
)
)
)
}
)
.noRippleClickable { .noRippleClickable {
if (memoryText.isNotEmpty() && !viewModel.isAddingMemory) { if (isButtonEnabled) {
onAddMemory(memoryText) onAddMemory(memoryText)
} }
}, },
@@ -920,7 +967,7 @@ fun AddGroupMemoryDialog(
text = stringResource(R.string.group_chat_info_add_memory), text = stringResource(R.string.group_chat_info_add_memory),
style = TextStyle( style = TextStyle(
fontSize = 17.sp, fontSize = 17.sp,
color = Color.White color = if (isButtonEnabled) Color.White else Color.White.copy(alpha = 0.6f)
) )
) )
} }
@@ -928,3 +975,200 @@ fun AddGroupMemoryDialog(
} }
} }
} }
@Composable
fun GroupVisibilityDialog(
onDismiss: () -> Unit
) {
val AppColors = LocalAppTheme.current
var isPrivate by remember { mutableStateOf(false) }
val balance = 482
val unlockCost = 500
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 16.dp)
.padding(bottom = 32.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_permission_settings),
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(6.dp))
// 公开群组
VisibilityOptionItem(
title = stringResource(R.string.group_chat_info_public_group),
desc = stringResource(R.string.group_chat_info_public_group_desc),
badge = null,
selected = !isPrivate,
onClick = { isPrivate = false }
)
Spacer(modifier = Modifier.height(16.dp))
// 私密群组
VisibilityOptionItem(
title = stringResource(R.string.group_chat_info_private_group),
desc = stringResource(R.string.group_chat_info_private_group_desc),
badge = stringResource(R.string.group_chat_info_private_group_cost),
selected = isPrivate,
onClick = { isPrivate = true }
)
Spacer(modifier = Modifier.height(16.dp))
// 余额与费用
Row(
modifier = Modifier.fillMaxWidth(),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Text(
text = stringResource(R.string.group_chat_info_balance, balance),
style = TextStyle(
fontSize = 13.sp,
color = Color(0x993C3C43)
)
)
Text(
text = stringResource(R.string.group_chat_info_unlock_cost, unlockCost),
style = TextStyle(
fontSize = 13.sp,
color = Color(0xFF9284BD)
)
)
}
Spacer(modifier = Modifier.height(8.dp))
// 完成按钮
Box(
modifier = Modifier
.fillMaxWidth()
.height(50.dp)
.clip(RoundedCornerShape(1000.dp))
.background(
brush = Brush.horizontalGradient(
colors = listOf(
Color(0xFF7C45ED),
Color(0xFF7C57EE),
Color(0xFF7BD8F8)
)
)
)
.noRippleClickable { onDismiss() },
contentAlignment = Alignment.Center
) {
Text(
text = stringResource(R.string.group_chat_info_done),
style = TextStyle(fontSize = 17.sp, color = Color.White)
)
}
Spacer(modifier = Modifier.height(8.dp))
Text(
text = stringResource(R.string.group_chat_info_recharge_hint),
style = TextStyle(fontSize = 12.sp, color = Color(0x4D3C3C43)),
textAlign = TextAlign.Center
)
}
}
@Composable
private fun VisibilityOptionItem(
title: String,
desc: String,
badge: String?,
selected: Boolean,
onClick: () -> Unit
) {
val borderColor = Color(0x1F7D767F)
Row(
modifier = Modifier
.fillMaxWidth()
.height(68.dp)
.clip(RoundedCornerShape(16.dp))
.background(Color.White)
.padding(horizontal = 12.dp)
.noRippleClickable { onClick() },
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Row(verticalAlignment = Alignment.CenterVertically) {
Box(
modifier = Modifier
.size(32.dp)
.clip(RoundedCornerShape(10.dp))
.background(Color(0x0F7C45ED)),
contentAlignment = Alignment.Center
) {
Text(text = if (title.contains("私密")) "🔒" else "🌐", fontSize = 16.sp)
}
Spacer(modifier = Modifier.width(10.dp))
Column(modifier = Modifier.widthIn(max = 240.dp)) {
Text(text = title, style = TextStyle(fontSize = 15.sp, color = Color.Black))
Spacer(modifier = Modifier.height(4.dp))
Text(
text = desc,
style = TextStyle(fontSize = 12.sp, color = Color(0x993C3C43))
)
}
}
Spacer(modifier = Modifier.weight(1f))
Row(verticalAlignment = Alignment.CenterVertically) {
if (badge != null) {
Box(
modifier = Modifier
.clip(RoundedCornerShape(8.dp))
.background(Color(0x1AFEBC2F))
.padding(horizontal = 10.dp, vertical = 4.dp)
) {
Text(
text = badge,
style = TextStyle(fontSize = 15.sp, color = Color(0xFF008D28))
)
}
Spacer(modifier = Modifier.width(8.dp))
}
if (selected) {
Box(
modifier = Modifier
.size(22.dp)
.clip(CircleShape)
.background(Color(0xFF34C759)),
contentAlignment = Alignment.Center
) {
Text(text = "", color = Color.White, fontSize = 14.sp)
}
}
}
}
}

View File

@@ -279,4 +279,42 @@ class GroupChatInfoViewModel(
} }
} }
} }
/**
* 更新记忆
*/
fun updateMemory(ruleId: Int, newRuleText: String, targetOpenId: String? = null) {
viewModelScope.launch {
try {
isLoadingMemory = true
memoryError = null
val openId = targetOpenId ?: promptOpenId
?: throw Exception("无法获取智能体ID")
val requestBody = com.aiosman.ravenow.data.api.UpdatePromptRuleRequestBody(
id = ruleId,
rule = newRuleText,
openId = openId
)
val response = ApiClient.api.updatePromptRule(requestBody)
if (response.isSuccessful) {
// 刷新记忆列表和配额
loadMemoryQuota(openId)
loadMemoryList(openId)
} else {
val errorResponse = parseErrorResponse(response.errorBody())
val errorMessage = errorResponse?.toServiceException()?.message
?: "更新记忆失败: ${response.code()}"
throw Exception(errorMessage)
}
} catch (e: Exception) {
memoryError = e.message ?: "更新记忆失败"
Log.e("GroupChatInfoViewModel", "更新记忆失败: ${e.message}", e)
} finally {
isLoadingMemory = false
}
}
}
} }

View File

@@ -27,17 +27,34 @@ import com.aiosman.ravenow.LocalAppTheme
import com.aiosman.ravenow.R import com.aiosman.ravenow.R
import com.aiosman.ravenow.ui.modifiers.noRippleClickable import com.aiosman.ravenow.ui.modifiers.noRippleClickable
import androidx.compose.foundation.Image 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 androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember
import androidx.compose.runtime.setValue
import androidx.compose.ui.graphics.SolidColor
import androidx.compose.ui.platform.LocalContext
import android.widget.Toast
import androidx.compose.ui.graphics.Brush
@Composable @Composable
fun GroupMemoryManageContent( fun GroupMemoryManageContent(
groupId: String, groupId: String,
viewModel: GroupChatInfoViewModel, viewModel: GroupChatInfoViewModel,
onAddMemoryClick: () -> Unit = {} onAddMemoryClick: () -> Unit = {},
onDismiss: () -> Unit = {}
) { ) {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
val configuration = LocalConfiguration.current val configuration = LocalConfiguration.current
val screenHeight = configuration.screenHeightDp.dp val screenHeight = configuration.screenHeightDp.dp
val sheetHeight = screenHeight * 0.9f val sheetHeight = screenHeight * 0.95f
val context = LocalContext.current
// 编辑记忆的状态 - 存储正在编辑的记忆ID
var editingMemoryId by remember { mutableStateOf<Int?>(null) }
// 加载配额和列表数据 // 加载配额和列表数据
LaunchedEffect(Unit) { LaunchedEffect(Unit) {
@@ -55,31 +72,70 @@ fun GroupMemoryManageContent(
.height(sheetHeight) .height(sheetHeight)
.background(Color(0xFFFAF9FB)) .background(Color(0xFFFAF9FB))
) { ) {
// 顶部栏:标题 + 加号(适配弹窗) // 顶部栏:返回按钮 + 标题 + 加号按钮
Row( Box(
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.height(44.dp) .height(44.dp)
.padding(horizontal = 16.dp), .padding(horizontal = 16.dp)
verticalAlignment = Alignment.CenterVertically
) { ) {
Box(modifier = Modifier.weight(1f), contentAlignment = Alignment.Center) { // 中间标题 - 绝对居中,不受其他组件影响
Text( Text(
text = "记忆管理", text = "记忆管理",
style = TextStyle(color = Color.Black, fontSize = 17.sp, fontWeight = FontWeight.SemiBold), style = TextStyle(
color = Color.Black,
fontSize = 17.sp,
fontWeight = FontWeight.SemiBold
),
modifier = Modifier.align(Alignment.Center),
textAlign = TextAlign.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
)
)
} }
// 右上角 + 按钮 // 右侧圆形加号按钮
Row( Box(
modifier = Modifier modifier = Modifier
.clip(RoundedCornerShape(296.dp)) .align(Alignment.CenterEnd)
.noRippleClickable { onAddMemoryClick() } .size(32.dp)
.padding(10.dp), .clip(CircleShape)
verticalAlignment = Alignment.CenterVertically .background(Color.White)
.noRippleClickable { onAddMemoryClick() },
contentAlignment = Alignment.Center
) { ) {
Text(text = "+", style = TextStyle(color = Color.Black, fontSize = 20.sp, fontWeight = FontWeight.Medium)) Text(
text = "+",
style = TextStyle(
color = Color.Black,
fontSize = 20.sp,
fontWeight = FontWeight.Medium
)
)
} }
} }
@@ -114,7 +170,7 @@ fun GroupMemoryManageContent(
Text("可用上限:", style = TextStyle(color = Color(0x993C3C43), fontSize = 13.sp)) Text("可用上限:", style = TextStyle(color = Color(0x993C3C43), fontSize = 13.sp))
Spacer(Modifier.width(3.dp)) Spacer(Modifier.width(3.dp))
Text( Text(
"${quota?.totalMaxCount ?: 0}", "50",
style = TextStyle(color = Color(0xFFFF8D28), fontSize = 13.sp) style = TextStyle(color = Color(0xFFFF8D28), fontSize = 13.sp)
) )
} }
@@ -147,6 +203,17 @@ fun GroupMemoryManageContent(
items(memoryList) { memory -> items(memoryList) { memory ->
MemoryItem( MemoryItem(
memory = memory, memory = memory,
isEditing = editingMemoryId == memory.id,
onEdit = {
editingMemoryId = memory.id
},
onCancel = {
editingMemoryId = null
},
onSave = { newText ->
viewModel.updateMemory(memory.id, newText)
editingMemoryId = null
},
onDelete = { onDelete = {
viewModel.deleteMemory(memory.id) viewModel.deleteMemory(memory.id)
} }
@@ -154,7 +221,6 @@ fun GroupMemoryManageContent(
} }
} }
} else { } else {
// 空状态
Column( Column(
modifier = Modifier modifier = Modifier
.fillMaxSize() .fillMaxSize()
@@ -162,40 +228,14 @@ fun GroupMemoryManageContent(
horizontalAlignment = Alignment.CenterHorizontally, horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center verticalArrangement = Arrangement.Center
) { ) {
// 简化的图形占位:橙色与紫色对话气泡 Image(
Box(contentAlignment = Alignment.Center) { painter = painterResource(id = R.mipmap.group),
Box( contentDescription = "暂无记忆",
modifier = Modifier modifier = Modifier
.size(74.dp) .height(150.dp).width(180.dp)
.clip(RoundedCornerShape(20.dp))
.background(Color(0xFFD4D4FC))
.offset(x = 26.dp, y = (-18).dp)
) )
Row(
modifier = Modifier
.height(72.dp)
.clip(RoundedCornerShape(20.dp))
.background(Color(0xFFFA8334))
.padding(horizontal = 12.dp),
verticalAlignment = Alignment.CenterVertically
) {
Box(
modifier = Modifier
.size(22.dp)
.clip(CircleShape)
.background(Color.White)
)
Spacer(Modifier.width(12.dp))
Box(
modifier = Modifier
.size(22.dp)
.clip(CircleShape)
.background(Color.White)
)
}
}
Spacer(Modifier.height(24.dp)) Spacer(Modifier.height(10.dp))
Text( Text(
text = "暂无记忆", text = "暂无记忆",
style = TextStyle(color = Color.Black, fontSize = 16.sp, fontWeight = FontWeight.SemiBold) style = TextStyle(color = Color.Black, fontSize = 16.sp, fontWeight = FontWeight.SemiBold)
@@ -211,57 +251,375 @@ fun GroupMemoryManageContent(
} }
} }
/**
* 编辑记忆对话框
*/
@OptIn(ExperimentalMaterial3Api::class)
@Composable
fun EditGroupMemoryDialog(
memory: com.aiosman.ravenow.data.api.PromptRule,
viewModel: GroupChatInfoViewModel,
onDismiss: () -> Unit,
onUpdateMemory: (String) -> Unit
) {
val AppColors = LocalAppTheme.current
val context = LocalContext.current
var memoryText by remember { mutableStateOf(memory.rule) }
val maxLength = 500
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
val gradientColors = listOf(
Color(0xFF7C45ED),
Color(0xFF7C57EE),
Color(0xFF7BD8F8)
)
val gradientBrush = Brush.horizontalGradient(colors = gradientColors)
ModalBottomSheet(
onDismissRequest = onDismiss,
sheetState = sheetState,
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)
) {
Column(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 12.dp, vertical = 16.dp),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.spacedBy(12.dp)
) {
Box(
modifier = Modifier
.fillMaxWidth()
.height(88.dp)
) {
Box(
modifier = Modifier
.fillMaxSize()
.clip(RoundedCornerShape(16.dp))
.background(brush = gradientBrush)
)
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 { onDismiss() },
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) {
onUpdateMemory(memoryText)
Toast.makeText(context, "记忆更新成功", Toast.LENGTH_SHORT).show()
}
},
contentAlignment = Alignment.Center
) {
Text(
text = "保存",
style = TextStyle(
fontSize = 15.sp,
color = Color.White
)
)
}
}
}
}
}
/** /**
* 记忆项组件 * 记忆项组件
*/ */
@Composable @Composable
fun MemoryItem( fun MemoryItem(
memory: com.aiosman.ravenow.data.api.PromptRule, memory: com.aiosman.ravenow.data.api.PromptRule,
isEditing: Boolean = false,
onEdit: () -> Unit = {},
onCancel: () -> Unit = {},
onSave: (String) -> Unit = {},
onDelete: () -> Unit onDelete: () -> Unit
) { ) {
val AppColors = LocalAppTheme.current val AppColors = LocalAppTheme.current
val context = LocalContext.current
var memoryText by remember { mutableStateOf(memory.rule) }
val maxLength = 500
Row( // 渐变边框颜色
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 modifier = Modifier
.fillMaxWidth() .fillMaxWidth()
.clip(RoundedCornerShape(12.dp)) .clip(RoundedCornerShape(16.dp))
.background(Color.White) .background(Color.White)
.padding(16.dp), .padding(horizontal = 12.dp, vertical = 16.dp),
verticalAlignment = Alignment.Top 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( Column(
modifier = Modifier.weight(1f) modifier = Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(16.dp))
.background(Color.White)
.padding(horizontal = 12.dp, vertical = 16.dp)
) { ) {
// 主文本 - 顶部
Text( Text(
text = memory.rule, text = memory.rule,
style = TextStyle( style = TextStyle(
color = Color.Black, color = Color.Black,
fontSize = 14.sp, fontSize = 13.sp,
lineHeight = 20.sp lineHeight = 18.sp
), ),
maxLines = 3, maxLines = 3,
overflow = TextOverflow.Ellipsis overflow = TextOverflow.Ellipsis
) )
Spacer(modifier = Modifier.height(8.dp))
Spacer(modifier = Modifier.height(16.dp))
// 底部行:日期 + 编辑删除按钮
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.Bottom
) {
// 日期文本 - 左侧
Text( Text(
text = "创建于 ${if (memory.createdAt.length >= 10) memory.createdAt.substring(0, 10) else memory.createdAt}", text = formattedDate,
style = TextStyle( style = TextStyle(
color = Color(0x993C3C43), color = Color(0x993C3C43),
fontSize = 12.sp fontSize = 11.sp
) )
) )
}
// 删除按钮 // 编辑和删除图标 - 右侧
Row(
horizontalArrangement = Arrangement.spacedBy(16.dp),
verticalAlignment = Alignment.CenterVertically
) {
// 编辑图标
Image( Image(
painter = painterResource(R.drawable.rider_pro_close), 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 = "删除", contentDescription = "删除",
modifier = Modifier modifier = Modifier
.size(20.dp) .size(20.dp)
.noRippleClickable { onDelete() }, .noRippleClickable { onDelete() },
colorFilter = ColorFilter.tint(Color(0x993C3C43)) colorFilter = ColorFilter.tint(Color(0xFFEE2A33))
) )
} }
} }
}
}
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.5 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 312 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 2.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 438 B

Binary file not shown.

After

Width:  |  Height:  |  Size: 3.7 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 575 B

View File

@@ -266,9 +266,20 @@
<string name="group_chat_info_dissolve">グループチャットを解散</string> <string name="group_chat_info_dissolve">グループチャットを解散</string>
<string name="group_chat_info_add_group_memory">グループメモリを追加</string> <string name="group_chat_info_add_group_memory">グループメモリを追加</string>
<string name="group_chat_info_member_count">%d人のメンバー</string> <string name="group_chat_info_member_count">%d人のメンバー</string>
<string name="group_chat_info_memory_input_hint">ここに入力した内容入力ボックスを超えると自動的に折り返されます</string> <string name="group_chat_info_memory_input_hint">グループの記憶内容入力、例えば、グループのメンバーは科学技術と設計を議論するのが好きである....</string>
<string name="group_chat_info_memory_cost">メモリを追加すると20コインを消費します</string> <string name="group_chat_info_memory_cost">メモリを追加すると20コインを消費します</string>
<string name="group_chat_info_memory_optimization">AIは記憶に基づいて返信を最適化します</string> <string name="group_chat_info_memory_optimization">AIは記憶に基づいて返信を最適化します</string>
<string name="group_chat_info_memory_editable">いつでも編集または削除できます</string> <string name="group_chat_info_memory_editable">いつでも編集または削除できます</string>
<string name="group_chat_info_memory_add_success">グループメモリが正常に追加されました</string>
<string name="group_chat_info_permission_settings">グループ権限設定</string>
<string name="group_chat_info_public_group">公開グループ</string>
<string name="group_chat_info_public_group_desc">誰でも検索して参加できます</string>
<string name="group_chat_info_private_group">プライベートグループ</string>
<string name="group_chat_info_private_group_desc">招待のみ</string>
<string name="group_chat_info_private_group_cost">50コイン</string>
<string name="group_chat_info_balance">残高: %1$dコイン</string>
<string name="group_chat_info_unlock_cost">アンロック費用: %1$dコイン</string>
<string name="group_chat_info_done">完了</string>
<string name="group_chat_info_recharge_hint">チャージしてより多くのコインを獲得できます</string>
</resources> </resources>

View File

@@ -269,9 +269,20 @@
<string name="group_chat_info_dissolve">解散群聊</string> <string name="group_chat_info_dissolve">解散群聊</string>
<string name="group_chat_info_add_group_memory">添加群记忆</string> <string name="group_chat_info_add_group_memory">添加群记忆</string>
<string name="group_chat_info_member_count">%d 位成员</string> <string name="group_chat_info_member_count">%d 位成员</string>
<string name="group_chat_info_memory_input_hint">这里的内容超过输入框自动换行</string> <string name="group_chat_info_memory_input_hint">输入群记忆内容,例如:群成员喜欢讨论科技和设计....</string>
<string name="group_chat_info_memory_cost">添加记忆需消耗 20 派币</string> <string name="group_chat_info_memory_cost">添加记忆需消耗 20 派币</string>
<string name="group_chat_info_memory_optimization">AI 将基于记忆优化回复</string> <string name="group_chat_info_memory_optimization">AI 将基于记忆优化回复</string>
<string name="group_chat_info_memory_editable">可随时编辑或删除</string> <string name="group_chat_info_memory_editable">可随时编辑或删除</string>
<string name="group_chat_info_memory_add_success">群记忆添加成功</string>
<string name="group_chat_info_permission_settings">群权限设置</string>
<string name="group_chat_info_public_group">公开群组</string>
<string name="group_chat_info_public_group_desc">任何人都可搜索并加入</string>
<string name="group_chat_info_private_group">私密群组</string>
<string name="group_chat_info_private_group_desc">仅限邀请加入</string>
<string name="group_chat_info_private_group_cost">50 派币</string>
<string name="group_chat_info_balance">余额: %1$d 派币</string>
<string name="group_chat_info_unlock_cost">解锁费用: %1$d 派币</string>
<string name="group_chat_info_done">完成</string>
<string name="group_chat_info_recharge_hint">可通过充值获得更多派币</string>
</resources> </resources>

View File

@@ -264,8 +264,19 @@
<string name="group_chat_info_dissolve">Dissolve Group Chat</string> <string name="group_chat_info_dissolve">Dissolve Group Chat</string>
<string name="group_chat_info_add_group_memory">Add Group Memory</string> <string name="group_chat_info_add_group_memory">Add Group Memory</string>
<string name="group_chat_info_member_count">%d members</string> <string name="group_chat_info_member_count">%d members</string>
<string name="group_chat_info_memory_input_hint">Content here will automatically wrap when it exceeds the input box</string> <string name="group_chat_info_memory_input_hint">Input group memory content, for example: Group members enjoy discussing technology and design …</string>
<string name="group_chat_info_memory_cost">Adding memory consumes 20 coins</string> <string name="group_chat_info_memory_cost">Adding memory consumes 20 coins</string>
<string name="group_chat_info_memory_optimization">AI will optimize replies based on memory</string> <string name="group_chat_info_memory_optimization">AI will optimize replies based on memory</string>
<string name="group_chat_info_memory_editable">Can be edited or deleted at any time</string> <string name="group_chat_info_memory_editable">Can be edited or deleted at any time</string>
<string name="group_chat_info_memory_add_success">Group memory added successfully</string>
<string name="group_chat_info_permission_settings">Group Permission Settings</string>
<string name="group_chat_info_public_group">Public Group</string>
<string name="group_chat_info_public_group_desc">Anyone can search and join</string>
<string name="group_chat_info_private_group">Private Group</string>
<string name="group_chat_info_private_group_desc">Invitation only</string>
<string name="group_chat_info_private_group_cost">50 coins</string>
<string name="group_chat_info_balance">Balance: %1$d coins</string>
<string name="group_chat_info_unlock_cost">Unlock cost: %1$d coins</string>
<string name="group_chat_info_done">Done</string>
<string name="group_chat_info_recharge_hint">You can recharge to get more coins</string>
</resources> </resources>