feat: 添加章节管理功能以支持清除回忆

- 在数据库模型中新增 is_active 字段,用于标记章节是否启用。
- 添加数据库迁移脚本以更新现有章节,确保默认值为 TRUE。
- 更新章节相关的 API 以仅返回 active 章节,并实现清除章节的功能。
- 在 Android 客户端中实现清除章节的确认弹窗和相应的 API 调用,提升用户体验。
This commit is contained in:
penghanyuan
2026-02-14 10:57:51 +01:00
parent df91719a2f
commit 39736a2ae2
8 changed files with 180 additions and 20 deletions

View File

@@ -165,6 +165,20 @@ class ApiService(
}
}
/**
* 清除章节(将章节标记为 disabled
*/
suspend fun disableChapter(chapterId: String): Result<Unit> {
return try {
val response = client.delete("$BASE_URL/api/chapters/$chapterId") {
contentType(ContentType.Application.Json)
}
Result.success(Unit)
} catch (e: Exception) {
Result.failure(e)
}
}
suspend fun exportPdf(bookId: String, userId: String = "default_user"): Result<ByteArray> {
return try {
val response = client.post("$BASE_URL/api/books/export-pdf") {

View File

@@ -216,6 +216,50 @@ fun MyMemoirScreen(
)
}
// 当前章节是否有内容active
val hasActiveContent = remember(selectedChapter) {
selectedChapter?.content?.isNotBlank() == true
}
// 清除回忆确认弹窗状态
var showClearDialog by remember { mutableStateOf(false) }
// 清除回忆确认弹窗
if (showClearDialog) {
AlertDialog(
onDismissRequest = { showClearDialog = false },
title = {
Text(
text = "清除回忆",
fontWeight = FontWeight.SemiBold
)
},
text = {
Text("清除回忆会完全清除当前章节的内容,确定继续吗?")
},
confirmButton = {
TextButton(
onClick = {
showClearDialog = false
selectedChapter?.let { chapter ->
viewModel.disableChapter(chapter.id)
}
},
colors = ButtonDefaults.textButtonColors(
contentColor = MaterialTheme.colorScheme.error
)
) {
Text("确定清除")
}
},
dismissButton = {
TextButton(onClick = { showClearDialog = false }) {
Text("取消")
}
}
)
}
Column(
modifier = Modifier
.fillMaxSize()
@@ -228,26 +272,53 @@ fun MyMemoirScreen(
.windowInsetsPadding(WindowInsets.statusBars)
)
// 返回按钮
// 顶部导航栏:返回按钮 + 清除回忆按钮
Row(
modifier = Modifier
.fillMaxWidth()
.padding(AppDimensions.cardPadding)
.clickable { viewModel.clearSelection() },
verticalAlignment = Alignment.CenterVertically
.padding(AppDimensions.cardPadding),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween
) {
Icon(
imageVector = AppIcons.ArrowBack,
contentDescription = "返回",
tint = SlatePurple,
modifier = Modifier.size(AppDimensions.iconSize)
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = "返回目录",
fontSize = AppTypography.bodyMedium,
color = SlatePurple
)
// 返回按钮
Row(
modifier = Modifier.clickable { viewModel.clearSelection() },
verticalAlignment = Alignment.CenterVertically
) {
Icon(
imageVector = AppIcons.ArrowBack,
contentDescription = "返回",
tint = SlatePurple,
modifier = Modifier.size(AppDimensions.iconSize)
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = "返回目录",
fontSize = AppTypography.bodyMedium,
color = SlatePurple
)
}
// 清除回忆按钮
TextButton(
onClick = { showClearDialog = true },
enabled = hasActiveContent,
colors = ButtonDefaults.textButtonColors(
contentColor = MaterialTheme.colorScheme.error,
disabledContentColor = SlatePurple.copy(alpha = 0.3f)
)
) {
Icon(
imageVector = AppIcons.Delete,
contentDescription = "清除回忆",
modifier = Modifier.size(AppDimensions.iconSizeSmall)
)
Spacer(modifier = Modifier.width(4.dp))
Text(
text = "清除回忆",
fontSize = AppTypography.captionLarge
)
}
}
ChapterReadingView(chapter = chapterContent)

View File

@@ -171,5 +171,38 @@ class MyMemoirViewModel(
fun toggleFullTextReading() {
showFullTextReading.value = !showFullTextReading.value
}
/**
* 清除章节(将章节标记为 disabled
* 成功后刷新章节列表并清除选中状态
*/
@RequiresApi(Build.VERSION_CODES.O)
fun disableChapter(chapterId: String, onSuccess: () -> Unit = {}, onError: (String) -> Unit = {}) {
viewModelScope.launch {
isLoading.value = true
try {
apiService.disableChapter(chapterId).fold(
onSuccess = {
// 清除选中状态,回到目录
clearSelection()
// 刷新章节列表
refreshChapters()
onSuccess()
},
onFailure = { e ->
val errorMsg = "清除回忆失败: ${e.message}"
error.value = errorMsg
onError(errorMsg)
}
)
} catch (e: Exception) {
val errorMsg = "清除回忆失败: ${e.message}"
error.value = errorMsg
onError(errorMsg)
} finally {
isLoading.value = false
}
}
}
}