feat: 支持在分类管理页面修改已有分类

This commit is contained in:
Super12138 2025-08-01 11:15:11 +08:00
parent 839b2a02e1
commit b92a62e57f
3 changed files with 59 additions and 34 deletions

View file

@ -47,13 +47,14 @@ fun SettingsDataCategory(
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
// TODO: 本页及其相关组件重组性能检查优化 // TODO: 本页及其相关组件重组性能检查优化
// TODO: 优化内部项目样式
val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior() val scrollBehavior = TopAppBarDefaults.exitUntilCollapsedScrollBehavior()
val snackbarHostState = remember { SnackbarHostState() } val snackbarHostState = remember { SnackbarHostState() }
val scope = rememberCoroutineScope() val scope = rememberCoroutineScope()
val listState = rememberLazyListState() val listState = rememberLazyListState()
var initialCategory by rememberSaveable { mutableStateOf("") }
var showDialog by rememberSaveable { mutableStateOf(false) } var showDialog by rememberSaveable { mutableStateOf(false) }
val categories by DataStoreManager.categoriesFlow.collectAsState(initial = emptyList()) val categories by DataStoreManager.categoriesFlow.collectAsState(initial = emptyList())
val isExpanded by remember { derivedStateOf { listState.firstVisibleItemIndex == 0 } } val isExpanded by remember { derivedStateOf { listState.firstVisibleItemIndex == 0 } }
@ -68,7 +69,10 @@ fun SettingsDataCategory(
icon = Icons.Outlined.Add, icon = Icons.Outlined.Add,
text = stringResource(R.string.action_add_category), text = stringResource(R.string.action_add_category),
expanded = isExpanded, expanded = isExpanded,
onClick = { showDialog = true } onClick = {
initialCategory = ""
showDialog = true
}
) )
}, },
modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection), modifier = modifier.nestedScroll(scrollBehavior.nestedScrollConnection),
@ -92,8 +96,12 @@ fun SettingsDataCategory(
items(items = categories, key = { it }) { items(items = categories, key = { it }) {
CategoryItem( CategoryItem(
name = it, name = it,
onDelete = { it -> onClick = { category ->
scope.launch { DataStoreManager.setCategories(categories - it) } initialCategory = category
showDialog = true
},
onDelete = { category ->
scope.launch { DataStoreManager.setCategories(categories - category) }
}, },
modifier = Modifier.animateItem( modifier = Modifier.animateItem(
fadeInSpec = tween(100), fadeInSpec = tween(100),
@ -110,20 +118,29 @@ fun SettingsDataCategory(
CategoryPromptDialog( CategoryPromptDialog(
visible = showDialog, visible = showDialog,
text = stringResource(R.string.tip_enter_category), initialCategory = initialCategory,
onSave = { onSave = { oldCategory, newCategory ->
if (!categories.contains(it)) { if (oldCategory.isEmpty()) {
scope.launch { if (!categories.contains(newCategory)) {
DataStoreManager.setCategories(categories + it) scope.launch {
} DataStoreManager.setCategories(categories + newCategory)
} else { }
scope.launch { } else {
/*snackbarHostState.showSnackbar( scope.launch {
/*snackbarHostState.showSnackbar(
message = context.getString(R.string.error_category_duplicate) message = context.getString(R.string.error_category_duplicate)
)*/ )*/
// 调换分类位置 // 调换分类位置
val tempList = categories - it val tempList = categories - newCategory
DataStoreManager.setCategories(tempList + it) DataStoreManager.setCategories(tempList + newCategory)
}
}
} else {
if (oldCategory != newCategory) {
scope.launch {
val tempList = categories - oldCategory
DataStoreManager.setCategories(tempList + newCategory)
}
} }
} }
}, },

View file

@ -6,18 +6,19 @@ import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.input.TextFieldLineLimits import androidx.compose.foundation.text.input.TextFieldLineLimits
import androidx.compose.foundation.text.input.clearText import androidx.compose.foundation.text.input.clearText
import androidx.compose.foundation.text.input.rememberTextFieldState import androidx.compose.foundation.text.input.rememberTextFieldState
import androidx.compose.foundation.text.input.setTextAndPlaceCursorAtEnd
import androidx.compose.material.icons.Icons import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.Info import androidx.compose.material.icons.outlined.Info
import androidx.compose.material3.OutlinedTextField import androidx.compose.material3.OutlinedTextField
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
import androidx.compose.runtime.remember import androidx.compose.runtime.remember
import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.runtime.setValue import androidx.compose.runtime.setValue
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.vector.ImageVector
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.DialogProperties import androidx.compose.ui.window.DialogProperties
@ -28,19 +29,20 @@ import cn.super12138.todo.ui.components.BasicDialog
fun CategoryPromptDialog( fun CategoryPromptDialog(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
visible: Boolean, visible: Boolean,
icon: ImageVector = Icons.Outlined.Info, initialCategory: String = "",
title: String = stringResource(R.string.tip_tips), onSave: (oldValue: String, newValue: String) -> Unit,
text: String,
confirmButtonText: String = stringResource(R.string.action_save),
showDismissButton: Boolean = true,
dismissButtonText: String = stringResource(R.string.action_cancel),
properties: DialogProperties = DialogProperties(),
onSave: (String) -> Unit,
onDismiss: () -> Unit onDismiss: () -> Unit
) { ) {
val textFieldState = rememberTextFieldState() val textFieldState = rememberTextFieldState(initialText = initialCategory)
var isError by rememberSaveable { mutableStateOf(false) } var isError by rememberSaveable { mutableStateOf(false) }
LaunchedEffect(visible) {
if (visible) {
textFieldState.setTextAndPlaceCursorAtEnd(initialCategory)
isError = false
}
}
val supportingText = listOf( val supportingText = listOf(
stringResource(R.string.tip_max_length_5), stringResource(R.string.tip_max_length_5),
stringResource(R.string.error_no_content_entered), stringResource(R.string.error_no_content_entered),
@ -51,11 +53,11 @@ fun CategoryPromptDialog(
BasicDialog( BasicDialog(
visible = visible, visible = visible,
icon = icon, icon = Icons.Outlined.Info,
title = title, title = stringResource(R.string.tip_tips),
text = { text = {
// 已经是实现好滚动的Column布局 // 已经是实现好滚动的Column布局
Text(text) Text(stringResource(R.string.tip_enter_category))
Spacer(Modifier.size(3.dp)) Spacer(Modifier.size(3.dp))
OutlinedTextField( OutlinedTextField(
state = textFieldState, state = textFieldState,
@ -65,8 +67,8 @@ fun CategoryPromptDialog(
isError = isError isError = isError
) )
}, },
confirmButton = confirmButtonText, confirmButton = stringResource(R.string.action_save),
dismissButton = if (showDismissButton) dismissButtonText else null, dismissButton = stringResource(R.string.action_cancel),
onConfirm = { onConfirm = {
val trimmedText = textFieldState.text.trim() val trimmedText = textFieldState.text.trim()
when { when {
@ -83,7 +85,7 @@ fun CategoryPromptDialog(
} }
else -> { else -> {
onSave(trimmedText.toString()) onSave(initialCategory, trimmedText.toString())
isError = false isError = false
currentSupportingText = supportingText[0] currentSupportingText = supportingText[0]
textFieldState.clearText() textFieldState.clearText()
@ -92,7 +94,7 @@ fun CategoryPromptDialog(
} }
}, },
onDismiss = onDismiss, onDismiss = onDismiss,
properties = properties, properties = DialogProperties(),
modifier = modifier modifier = modifier
) )
} }

View file

@ -18,7 +18,6 @@ import androidx.compose.ui.draw.clip
import androidx.compose.ui.platform.LocalView import androidx.compose.ui.platform.LocalView
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.sp
import cn.super12138.todo.R import cn.super12138.todo.R
import cn.super12138.todo.ui.TodoDefaults import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.utils.VibrationUtils import cn.super12138.todo.utils.VibrationUtils
@ -27,6 +26,7 @@ import cn.super12138.todo.utils.VibrationUtils
fun CategoryItem( fun CategoryItem(
modifier: Modifier = Modifier, modifier: Modifier = Modifier,
name: String, name: String,
onClick: (String) -> Unit = {},
onDelete: (String) -> Unit = {} onDelete: (String) -> Unit = {}
) { ) {
val view = LocalView.current val view = LocalView.current
@ -35,6 +35,12 @@ fun CategoryItem(
.fillMaxWidth() .fillMaxWidth()
.wrapContentHeight() .wrapContentHeight()
.clip(MaterialTheme.shapes.large) .clip(MaterialTheme.shapes.large)
.clickable(
onClick = {
VibrationUtils.performHapticFeedback(view)
onClick(name)
}
)
.padding( .padding(
horizontal = TodoDefaults.settingsItemHorizontalPadding, horizontal = TodoDefaults.settingsItemHorizontalPadding,
vertical = TodoDefaults.settingsItemVerticalPadding / 2 vertical = TodoDefaults.settingsItemVerticalPadding / 2