From 2959966552c338ba0a5b19cfd6365edf7eb3c5bb Mon Sep 17 00:00:00 2001 From: Super12138 <70494801+Super12138@users.noreply.github.com> Date: Mon, 9 Feb 2026 17:03:33 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20=E5=A2=9E=E5=8A=A0=E4=BB=8A=E6=97=A5?= =?UTF-8?q?=E4=BB=BB=E5=8A=A1=E8=BF=9B=E5=BA=A6=E6=9D=BF=E5=9D=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../super12138/todo/ui/components/EmptyTip.kt | 10 +- .../todo/ui/pages/overview/OverviewPage.kt | 39 ++++-- .../{UpcomingTaskCard.kt => ListCard.kt} | 19 ++- .../pages/overview/components/ProgressCard.kt | 130 ++++++++++++++++++ .../cn/super12138/todo/utils/SystemUtils.kt | 1 - app/src/main/res/values-zh-rCN/strings.xml | 2 +- app/src/main/res/values/strings.xml | 2 +- 7 files changed, 182 insertions(+), 21 deletions(-) rename app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/components/{UpcomingTaskCard.kt => ListCard.kt} (92%) create mode 100644 app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/components/ProgressCard.kt diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/components/EmptyTip.kt b/app/src/main/kotlin/cn/super12138/todo/ui/components/EmptyTip.kt index 6f727f0..e76ffe9 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/components/EmptyTip.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/components/EmptyTip.kt @@ -7,10 +7,12 @@ import androidx.compose.foundation.layout.size import androidx.compose.foundation.shape.CircleShape import androidx.compose.material3.Icon import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.contentColorFor import androidx.compose.runtime.Composable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip +import androidx.compose.ui.graphics.Color import androidx.compose.ui.res.painterResource import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp @@ -26,8 +28,10 @@ enum class EmptyTipType { @Composable fun EmptyTip( modifier: Modifier = Modifier, + type: EmptyTipType, size: Dp = 48.dp, - type: EmptyTipType + containerColor: Color = MaterialTheme.colorScheme.secondaryContainer, + contentColor: Color = contentColorFor(containerColor) ) { Box( contentAlignment = Alignment.Center, @@ -35,7 +39,7 @@ fun EmptyTip( .padding(TodoDefaults.screenHorizontalPadding) .size(size) .clip(CircleShape) - .background(MaterialTheme.colorScheme.secondaryContainer) + .background(containerColor) ) { Icon( painter = painterResource( @@ -46,7 +50,7 @@ fun EmptyTip( } ), contentDescription = null, - tint = MaterialTheme.colorScheme.secondary, + tint = contentColor, modifier = Modifier.size(size / 2) ) } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/OverviewPage.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/OverviewPage.kt index a5efe53..b2dfa94 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/OverviewPage.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/OverviewPage.kt @@ -17,8 +17,9 @@ import androidx.compose.ui.res.stringResource import androidx.compose.ui.unit.dp import cn.super12138.todo.R import cn.super12138.todo.ui.components.TopAppBarScaffold +import cn.super12138.todo.ui.pages.overview.components.ListCard +import cn.super12138.todo.ui.pages.overview.components.ProgressCard import cn.super12138.todo.ui.pages.overview.components.RoundedCornerCardLarge -import cn.super12138.todo.ui.pages.overview.components.UpcomingTaskCard import cn.super12138.todo.ui.viewmodels.MainViewModel import cn.super12138.todo.utils.SystemUtils @@ -31,13 +32,26 @@ fun OverviewPage( val toDos by viewModel.sortedTodos.collectAsState(initial = emptyList()) val totalTasks by remember { derivedStateOf { toDos.size } } val completedTasks by remember { derivedStateOf { toDos.count { it.isCompleted } } } - val nextWeekTodo by remember { + + val todayMillis = SystemUtils.getToday() + val dayMillis = 24L * 60 * 60 * 1000 + val todayEnd = todayMillis + dayMillis + + val todayTodo by remember { derivedStateOf { - val today = SystemUtils.getToday() - val weekFromToday = today + 7L * 24 * 60 * 60 * 1000 toDos.filter { todo -> val due = todo.dueDate ?: return@filter false - due in today.. + val due = todo.dueDate ?: return@filter false + due in todayMillis until weekFromToday } } } @@ -74,12 +88,21 @@ fun OverviewPage( iconRes = R.drawable.ic_pending, title = stringResource(R.string.title_pending_task), count = totalTasks - completedTasks, - containerColor = MaterialTheme.colorScheme.errorContainer // tertiaryContainer + containerColor = MaterialTheme.colorScheme.errorContainer ) } item { - UpcomingTaskCard( - nextWeekTodo = nextWeekTodo + ProgressCard( + title = stringResource(R.string.title_today_task), + total = todayTodo.size, + completed = todayTodo.count { it.isCompleted } + ) + } + + item { + ListCard( + title = stringResource(R.string.title_upcoming_task), + list = nextWeekTodo ) } } diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/components/UpcomingTaskCard.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/components/ListCard.kt similarity index 92% rename from app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/components/UpcomingTaskCard.kt rename to app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/components/ListCard.kt index 6110f21..4f4b81e 100644 --- a/app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/components/UpcomingTaskCard.kt +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/components/ListCard.kt @@ -40,10 +40,12 @@ import cn.super12138.todo.utils.containerColor import cn.super12138.todo.utils.toRelativeTimeString @Composable -fun UpcomingTaskCard( +fun ListCard( modifier: Modifier = Modifier, - nextWeekTodo: List, - containerColor: Color = TodoDefaults.Colors.Container + title: String, + list: List, + containerColor: Color = TodoDefaults.Colors.Container, + emptyTipContainerColor: Color = MaterialTheme.colorScheme.secondaryContainer ) { Card( modifier = modifier.height(TodoDefaults.overviewCardHeight * 2), @@ -58,12 +60,12 @@ fun UpcomingTaskCard( verticalArrangement = Arrangement.spacedBy(8.dp) ) { Text( - text = stringResource(R.string.title_upcoming_task), + text = title, style = MaterialTheme.typography.titleLarge ) val transitionSpec = fadeScale() AnimatedContent( - targetState = nextWeekTodo.isEmpty(), + targetState = list.isEmpty(), transitionSpec = { transitionSpec } ) { if (it) { @@ -75,7 +77,10 @@ fun UpcomingTaskCard( verticalArrangement = Arrangement.Center, horizontalAlignment = Alignment.CenterHorizontally ) { - EmptyTip(type = EmptyTipType.List) + EmptyTip( + type = EmptyTipType.List, + containerColor = emptyTipContainerColor + ) Text( text = stringResource(R.string.tip_no_task_brief), @@ -87,7 +92,7 @@ fun UpcomingTaskCard( } else { LazyColumn(verticalArrangement = Arrangement.spacedBy(8.dp)) { items( - items = nextWeekTodo, + items = list, key = { task -> task.id } ) { task -> UpcomingTaskItem( diff --git a/app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/components/ProgressCard.kt b/app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/components/ProgressCard.kt new file mode 100644 index 0000000..c0cbb46 --- /dev/null +++ b/app/src/main/kotlin/cn/super12138/todo/ui/pages/overview/components/ProgressCard.kt @@ -0,0 +1,130 @@ +package cn.super12138.todo.ui.pages.overview.components + +import androidx.compose.animation.AnimatedContent +import androidx.compose.animation.core.animateFloatAsState +import androidx.compose.foundation.layout.Arrangement +import androidx.compose.foundation.layout.Column +import androidx.compose.foundation.layout.fillMaxSize +import androidx.compose.foundation.layout.fillMaxWidth +import androidx.compose.foundation.layout.height +import androidx.compose.foundation.layout.padding +import androidx.compose.foundation.layout.size +import androidx.compose.foundation.rememberScrollState +import androidx.compose.foundation.verticalScroll +import androidx.compose.material3.Card +import androidx.compose.material3.CardDefaults +import androidx.compose.material3.CircularWavyProgressIndicator +import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi +import androidx.compose.material3.MaterialTheme +import androidx.compose.material3.ProgressIndicatorDefaults +import androidx.compose.material3.Text +import androidx.compose.runtime.Composable +import androidx.compose.runtime.getValue +import androidx.compose.runtime.remember +import androidx.compose.ui.Alignment +import androidx.compose.ui.Modifier +import androidx.compose.ui.graphics.Color +import androidx.compose.ui.graphics.StrokeCap +import androidx.compose.ui.graphics.drawscope.Stroke +import androidx.compose.ui.platform.LocalDensity +import androidx.compose.ui.res.stringResource +import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp +import cn.super12138.todo.R +import cn.super12138.todo.ui.TodoDefaults +import cn.super12138.todo.ui.components.EmptyTip +import cn.super12138.todo.ui.components.EmptyTipType +import cn.super12138.todo.ui.theme.fadeScale + +@OptIn(ExperimentalMaterial3ExpressiveApi::class) +@Composable +fun ProgressCard( + modifier: Modifier = Modifier, + title: String, + total: Int, + completed: Int, + containerColor: Color = TodoDefaults.Colors.Container, + emptyTipContainerColor: Color = MaterialTheme.colorScheme.secondaryContainer +) { + val progress = if (total == 0) 0f else completed / total.toFloat() + + Card( + modifier = modifier.height(TodoDefaults.overviewCardHeight * 2), + colors = CardDefaults.cardColors(containerColor = containerColor), + shape = TodoDefaults.defaultShape + ) { + Column( + modifier = Modifier + .fillMaxWidth() + .padding(TodoDefaults.screenHorizontalPadding), + horizontalAlignment = Alignment.Start, + verticalArrangement = Arrangement.spacedBy(8.dp) + ) { + Text( + text = title, + style = MaterialTheme.typography.titleLarge + ) + val transitionSpec = fadeScale() + AnimatedContent( + targetState = total == 0, + transitionSpec = { transitionSpec } + ) { + if (it) { + Column( + modifier = Modifier + .fillMaxSize() + .weight(1f) // 占满剩余空间 + .verticalScroll(rememberScrollState()), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + EmptyTip( + type = EmptyTipType.List, + containerColor = emptyTipContainerColor + ) + + Text( + text = stringResource(R.string.tip_no_task_brief), + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + } + } else { + Column( + modifier = Modifier.fillMaxSize(), + horizontalAlignment = Alignment.CenterHorizontally, + verticalArrangement = Arrangement.Center + ) { + val animatedProgress by animateFloatAsState( + targetValue = progress, + animationSpec = ProgressIndicatorDefaults.ProgressAnimationSpec, + ) + val thickStrokeWidth = with(LocalDensity.current) { 5.dp.toPx() } + val thickStroke = remember(thickStrokeWidth) { + Stroke(width = thickStrokeWidth, cap = StrokeCap.Round) + } + + CircularWavyProgressIndicator( + progress = { animatedProgress }, + waveSpeed = 3.dp, + wavelength = 20.dp, + stroke = thickStroke, + trackStroke = thickStroke, + modifier = Modifier + .padding(TodoDefaults.screenHorizontalPadding) + .size(90.dp) + ) + + Text( + text = "$completed / $total", + style = MaterialTheme.typography.labelLarge, + textAlign = TextAlign.Center, + modifier = Modifier.fillMaxWidth() + ) + } + } + } + } + } +} \ No newline at end of file diff --git a/app/src/main/kotlin/cn/super12138/todo/utils/SystemUtils.kt b/app/src/main/kotlin/cn/super12138/todo/utils/SystemUtils.kt index 3a52a26..bf2c42e 100644 --- a/app/src/main/kotlin/cn/super12138/todo/utils/SystemUtils.kt +++ b/app/src/main/kotlin/cn/super12138/todo/utils/SystemUtils.kt @@ -53,7 +53,6 @@ object SystemUtils { set(Calendar.SECOND, 0) set(Calendar.MILLISECOND, 0) }.timeInMillis - } fun ComponentActivity.configureEdgeToEdge() { diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml index 973cf30..387f0b9 100644 --- a/app/src/main/res/values-zh-rCN/strings.xml +++ b/app/src/main/res/values-zh-rCN/strings.xml @@ -134,5 +134,5 @@ 截止日期 暂未搜索到内容 暂无任务 - 今日任务 + 今日任务进度 \ No newline at end of file diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 4ff12bf..94945db 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -135,5 +135,5 @@ Due date No tasks found No tasks - Today + Today\'s Task Progress \ No newline at end of file