群记忆项编辑功能;群权限设置UI;群记忆管理缺省图
This commit is contained in:
@@ -63,6 +63,7 @@ fun GroupChatInfoScreen(groupId: String) {
|
||||
|
||||
var showAddMemoryDialog by remember { mutableStateOf(false) }
|
||||
var showMemoryManageDialog by remember { mutableStateOf(false) }
|
||||
var showVisibilityDialog by remember { mutableStateOf(false) }
|
||||
val sheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
val memoryManageSheetState = rememberModalBottomSheetState(skipPartiallyExpanded = true)
|
||||
|
||||
@@ -150,7 +151,7 @@ fun GroupChatInfoScreen(groupId: String) {
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
|
||||
Text(
|
||||
text = viewModel.groupInfo?.groupName ?: "",
|
||||
text = viewModel.groupInfo?.groupName ?: stringResource(R.string.group_info_edit),
|
||||
style = androidx.compose.ui.text.TextStyle(
|
||||
color = AppColors.text,
|
||||
fontSize = 16.sp,
|
||||
@@ -167,7 +168,7 @@ fun GroupChatInfoScreen(groupId: String) {
|
||||
)
|
||||
Spacer(modifier = Modifier.width(4.dp))
|
||||
Text(
|
||||
text = "4人",
|
||||
text = "${viewModel.groupInfo?.memberCount ?: 0}人",
|
||||
style = androidx.compose.ui.text.TextStyle(
|
||||
color = AppColors.text.copy(alpha = 0.7f),
|
||||
fontSize = 11.sp
|
||||
@@ -435,6 +436,7 @@ fun GroupChatInfoScreen(groupId: String) {
|
||||
.clip(RoundedCornerShape(8.dp))
|
||||
.padding(12.dp)
|
||||
.noRippleClickable {
|
||||
showVisibilityDialog = true
|
||||
},
|
||||
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) {
|
||||
ModalBottomSheet(
|
||||
@@ -623,6 +652,9 @@ fun GroupChatInfoScreen(groupId: String) {
|
||||
onAddMemoryClick = {
|
||||
showMemoryManageDialog = false
|
||||
showAddMemoryDialog = true
|
||||
},
|
||||
onDismiss = {
|
||||
showMemoryManageDialog = false
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -645,7 +677,7 @@ fun AddGroupMemoryDialog(
|
||||
// 监听添加记忆的结果
|
||||
LaunchedEffect(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 = "" // 清空输入框
|
||||
onDismiss()
|
||||
viewModel.addMemorySuccess = false
|
||||
@@ -745,7 +777,7 @@ fun AddGroupMemoryDialog(
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp)
|
||||
) {
|
||||
Text(
|
||||
text = groupInfo?.groupName ?: "创意项目讨论组",
|
||||
text = groupInfo?.groupName ?: "",
|
||||
style = TextStyle(
|
||||
fontSize = 15.sp,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
@@ -889,22 +921,37 @@ fun AddGroupMemoryDialog(
|
||||
}
|
||||
|
||||
// 添加记忆按钮
|
||||
val isButtonEnabled = memoryText.isNotEmpty() && !viewModel.isAddingMemory
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(50.dp)
|
||||
.clip(RoundedCornerShape(1000.dp))
|
||||
.background(
|
||||
brush = Brush.horizontalGradient(
|
||||
colors = listOf(
|
||||
Color(0xFF7C45ED), // 紫色
|
||||
Color(0xFF7C57EE), // 浅紫色
|
||||
Color(0xFF7BD8F8) // 蓝色
|
||||
.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 (memoryText.isNotEmpty() && !viewModel.isAddingMemory) {
|
||||
if (isButtonEnabled) {
|
||||
onAddMemory(memoryText)
|
||||
}
|
||||
},
|
||||
@@ -920,7 +967,7 @@ fun AddGroupMemoryDialog(
|
||||
text = stringResource(R.string.group_chat_info_add_memory),
|
||||
style = TextStyle(
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -27,17 +27,34 @@ import com.aiosman.ravenow.LocalAppTheme
|
||||
import com.aiosman.ravenow.R
|
||||
import com.aiosman.ravenow.ui.modifiers.noRippleClickable
|
||||
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
|
||||
fun GroupMemoryManageContent(
|
||||
groupId: String,
|
||||
viewModel: GroupChatInfoViewModel,
|
||||
onAddMemoryClick: () -> Unit = {}
|
||||
onAddMemoryClick: () -> Unit = {},
|
||||
onDismiss: () -> Unit = {}
|
||||
) {
|
||||
val AppColors = LocalAppTheme.current
|
||||
val configuration = LocalConfiguration.current
|
||||
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) {
|
||||
@@ -55,31 +72,70 @@ fun GroupMemoryManageContent(
|
||||
.height(sheetHeight)
|
||||
.background(Color(0xFFFAF9FB))
|
||||
) {
|
||||
// 顶部栏:标题 + 加号(适配弹窗)
|
||||
Row(
|
||||
// 顶部栏:返回按钮 + 标题 + 加号按钮
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.height(44.dp)
|
||||
.padding(horizontal = 16.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
.padding(horizontal = 16.dp)
|
||||
) {
|
||||
Box(modifier = Modifier.weight(1f), contentAlignment = Alignment.Center) {
|
||||
// 中间标题 - 绝对居中,不受其他组件影响
|
||||
Text(
|
||||
text = "记忆管理",
|
||||
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 = 17.sp, fontWeight = FontWeight.SemiBold),
|
||||
textAlign = TextAlign.Center
|
||||
text = "返回",
|
||||
style = TextStyle(
|
||||
color = Color.Black,
|
||||
fontSize = 15.sp,
|
||||
fontWeight = FontWeight.Normal
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// 右上角 + 按钮
|
||||
Row(
|
||||
// 右侧圆形加号按钮
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.clip(RoundedCornerShape(296.dp))
|
||||
.noRippleClickable { onAddMemoryClick() }
|
||||
.padding(10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
.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))
|
||||
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))
|
||||
Spacer(Modifier.width(3.dp))
|
||||
Text(
|
||||
"${quota?.totalMaxCount ?: 0}",
|
||||
"50",
|
||||
style = TextStyle(color = Color(0xFFFF8D28), fontSize = 13.sp)
|
||||
)
|
||||
}
|
||||
@@ -147,6 +203,17 @@ fun GroupMemoryManageContent(
|
||||
items(memoryList) { memory ->
|
||||
MemoryItem(
|
||||
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)
|
||||
}
|
||||
@@ -154,7 +221,6 @@ fun GroupMemoryManageContent(
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 空状态
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -162,40 +228,14 @@ fun GroupMemoryManageContent(
|
||||
horizontalAlignment = Alignment.CenterHorizontally,
|
||||
verticalArrangement = Arrangement.Center
|
||||
) {
|
||||
// 简化的图形占位:橙色与紫色对话气泡
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.size(74.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)
|
||||
)
|
||||
}
|
||||
}
|
||||
Image(
|
||||
painter = painterResource(id = R.mipmap.group),
|
||||
contentDescription = "暂无记忆",
|
||||
modifier = Modifier
|
||||
.height(150.dp).width(180.dp)
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(24.dp))
|
||||
Spacer(Modifier.height(10.dp))
|
||||
Text(
|
||||
text = "暂无记忆",
|
||||
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
|
||||
fun MemoryItem(
|
||||
memory: com.aiosman.ravenow.data.api.PromptRule,
|
||||
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
|
||||
|
||||
Row(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(12.dp))
|
||||
.background(Color.White)
|
||||
.padding(16.dp),
|
||||
verticalAlignment = Alignment.Top
|
||||
) {
|
||||
// 记忆内容
|
||||
// 渐变边框颜色
|
||||
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.weight(1f)
|
||||
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 = 14.sp,
|
||||
lineHeight = 20.sp
|
||||
fontSize = 13.sp,
|
||||
lineHeight = 18.sp
|
||||
),
|
||||
maxLines = 3,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
Spacer(modifier = Modifier.height(8.dp))
|
||||
Text(
|
||||
text = "创建于 ${if (memory.createdAt.length >= 10) memory.createdAt.substring(0, 10) else memory.createdAt}",
|
||||
style = TextStyle(
|
||||
color = Color(0x993C3C43),
|
||||
fontSize = 12.sp
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
// 删除按钮
|
||||
Image(
|
||||
painter = painterResource(R.drawable.rider_pro_close),
|
||||
contentDescription = "删除",
|
||||
modifier = Modifier
|
||||
.size(20.dp)
|
||||
.noRippleClickable { onDelete() },
|
||||
colorFilter = ColorFilter.tint(Color(0x993C3C43))
|
||||
)
|
||||
Spacer(modifier = Modifier.height(16.dp))
|
||||
|
||||
// 底部行:日期 + 编辑删除按钮
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceBetween,
|
||||
verticalAlignment = Alignment.Bottom
|
||||
) {
|
||||
// 日期文本 - 左侧
|
||||
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))
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
BIN
app/src/main/res/mipmap-xhdpi/group.png
Normal file
BIN
app/src/main/res/mipmap-xhdpi/group.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 1.5 KiB |
BIN
app/src/main/res/mipmap-xhdpi/iconsdelete.png
Normal file
BIN
app/src/main/res/mipmap-xhdpi/iconsdelete.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 312 B |
BIN
app/src/main/res/mipmap-xxhdpi/group.png
Normal file
BIN
app/src/main/res/mipmap-xxhdpi/group.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 2.7 KiB |
BIN
app/src/main/res/mipmap-xxhdpi/iconsdelete.png
Normal file
BIN
app/src/main/res/mipmap-xxhdpi/iconsdelete.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 438 B |
BIN
app/src/main/res/mipmap-xxxhdpi/group.png
Normal file
BIN
app/src/main/res/mipmap-xxxhdpi/group.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 3.7 KiB |
BIN
app/src/main/res/mipmap-xxxhdpi/iconsdelete.png
Normal file
BIN
app/src/main/res/mipmap-xxxhdpi/iconsdelete.png
Normal file
Binary file not shown.
|
After Width: | Height: | Size: 575 B |
@@ -266,9 +266,20 @@
|
||||
<string name="group_chat_info_dissolve">グループチャットを解散</string>
|
||||
<string name="group_chat_info_add_group_memory">グループメモリを追加</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_optimization">AIは記憶に基づいて返信を最適化します</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>
|
||||
|
||||
|
||||
@@ -269,9 +269,20 @@
|
||||
<string name="group_chat_info_dissolve">解散群聊</string>
|
||||
<string name="group_chat_info_add_group_memory">添加群记忆</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_optimization">AI 将基于记忆优化回复</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>
|
||||
@@ -264,8 +264,19 @@
|
||||
<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_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_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_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>
|
||||
Reference in New Issue
Block a user