feat: 进一步实现临近任务卡片

This commit is contained in:
Super12138 2026-02-08 12:51:35 +08:00
parent 2f3e84b8f9
commit b585565459
5 changed files with 132 additions and 40 deletions

View file

@ -38,25 +38,7 @@ object TodoDefaults {
val settingsItemPadding = 4.dp
/**
* 待办进度条粗度
*/
val trackThickness = 7.0.dp
/**
* 待办进度条波长
*/
val waveLength = 35.dp
/**
* 待办进度条波速
*/
val waveSpeed = 3.dp
/**
* 待办进度条波幅
*/
const val waveAmplitude = 0.6f
val overviewCardHeight = 120.dp
val ScreenContainerShape: Shape
@Composable

View file

@ -59,7 +59,7 @@ fun OverviewPage(
Column {
LazyVerticalStaggeredGrid(
modifier = Modifier.fillMaxSize(),
columns = StaggeredGridCells.Adaptive(150.dp),
columns = StaggeredGridCells.Adaptive(160.dp),
horizontalArrangement = Arrangement.spacedBy(10.dp),
verticalItemSpacing = 10.dp
) {

View file

@ -7,9 +7,13 @@ import androidx.compose.foundation.interaction.collectIsPressedAsState
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.text.BasicText
import androidx.compose.foundation.text.TextAutoSize
import androidx.compose.material3.ButtonShapes
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
@ -23,6 +27,7 @@ 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.ColorProducer
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
@ -44,14 +49,15 @@ fun RoundedCornerCardLarge(
val pressed by interactionSource.collectIsPressedAsState()
val animatedShape = shapeByInteraction(shapes, pressed, TodoDefaults.shapesDefaultAnimationSpec)
val cardColors = CardDefaults.cardColors(containerColor = containerColor)
Card(
modifier = modifier.wrapContentHeight(),
colors = CardDefaults.cardColors(containerColor = containerColor),
modifier = modifier.height(TodoDefaults.overviewCardHeight),
colors = cardColors,
shape = animatedShape
) {
Row(
modifier = Modifier
.fillMaxWidth()
.fillMaxSize()
.clickable(
enabled = true,
onClick = onClick,
@ -63,18 +69,29 @@ fun RoundedCornerCardLarge(
) {
Icon(
painter = painterResource(iconRes),
contentDescription = null
contentDescription = null,
modifier = Modifier.size(30.dp)
)
Column(horizontalAlignment = Alignment.Start) {
Column(
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.SpaceBetween,
modifier = Modifier
.fillMaxHeight()
.padding(vertical = 8.dp)
) {
Text(
text = title,
style = MaterialTheme.typography.bodyMedium
)
Text(
BasicText(
text = count.toString(),
style = MaterialTheme.typography.headlineLarge.copy(
fontWeight = FontWeight.ExtraBold
)
style = MaterialTheme.typography.headlineLarge.copy(fontWeight = FontWeight.Bold),
color = ColorProducer { cardColors.contentColor },
autoSize = TextAutoSize.StepBased(
MaterialTheme.typography.headlineSmall.fontSize,
MaterialTheme.typography.displayMedium.fontSize
),
)
}
}

View file

@ -2,11 +2,14 @@ package cn.super12138.todo.ui.pages.overview.components
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.wrapContentHeight
import androidx.compose.foundation.lazy.LazyColumn
import androidx.compose.foundation.lazy.items
import androidx.compose.material3.Badge
import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults
import androidx.compose.material3.MaterialTheme
@ -16,12 +19,15 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import cn.super12138.todo.R
import cn.super12138.todo.logic.database.TodoEntity
import cn.super12138.todo.logic.model.Priority
import cn.super12138.todo.ui.TodoDefaults
import cn.super12138.todo.ui.pages.settings.components.SettingsItem
import cn.super12138.todo.utils.toLocalDateString
import cn.super12138.todo.utils.containerColor
import cn.super12138.todo.utils.toRelativeTimeString
@Composable
fun UpcomingTaskCard(
@ -30,7 +36,7 @@ fun UpcomingTaskCard(
containerColor: Color = TodoDefaults.ContainerColor
) {
Card(
modifier = modifier.height(300.dp),
modifier = modifier.height(TodoDefaults.overviewCardHeight * 2),
colors = CardDefaults.cardColors(containerColor = containerColor),
shape = TodoDefaults.defaultShape
) {
@ -39,11 +45,11 @@ fun UpcomingTaskCard(
.fillMaxWidth()
.padding(TodoDefaults.screenHorizontalPadding),
horizontalAlignment = Alignment.Start,
verticalArrangement = Arrangement.spacedBy(16.dp)
verticalArrangement = Arrangement.spacedBy(8.dp)
) {
Text(
text = stringResource(R.string.title_upcoming_task),
style = MaterialTheme.typography.titleMedium
style = MaterialTheme.typography.titleLarge
)
LazyColumn(
@ -53,13 +59,72 @@ fun UpcomingTaskCard(
items = nextWeekTodo,
key = { it.id }
) {
SettingsItem(
title = it.content,
description = it.dueDate.toLocalDateString(),
enableClick = false
UpcomingTaskItem(
content = it.content,
category = it.category,
priority = Priority.fromFloat(it.priority),
dueDate = it.dueDate
)
}
}
}
}
}
@Composable
fun UpcomingTaskItem(
content: String,
category: String,
priority: Priority,
dueDate: Long?,
modifier: Modifier = Modifier
) {
Column(
modifier = modifier
.fillMaxWidth()
.wrapContentHeight()
.padding(vertical = TodoDefaults.settingsItemVerticalPadding / 4),
horizontalAlignment = Alignment.Start
) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.SpaceBetween,
modifier = Modifier.fillMaxWidth()
) {
Text(
text = content,
maxLines = 1,
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.titleMedium,
modifier = Modifier.weight(1f)
)
Text(
text = dueDate.toRelativeTimeString(),
style = MaterialTheme.typography.labelMedium.copy(
color = MaterialTheme.colorScheme.onSurfaceVariant,
fontWeight = FontWeight.Bold
),
modifier = Modifier.padding(start = TodoDefaults.screenVerticalPadding)
)
}
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.spacedBy(5.dp)
) {
Badge(containerColor = MaterialTheme.colorScheme.primary) {
Text(
text = category.ifEmpty { stringResource(R.string.tip_default_category) },
style = MaterialTheme.typography.labelMedium,
maxLines = 1
)
}
Text(
text = stringResource(priority.nameRes),
style = MaterialTheme.typography.labelMedium.copy(priority.containerColor()),
)
}
}
}

View file

@ -13,7 +13,10 @@ import androidx.compose.ui.graphics.drawscope.ContentDrawScope
import androidx.compose.ui.unit.Dp
import cn.super12138.todo.logic.model.Priority
import java.text.SimpleDateFormat
import java.util.Calendar
import java.util.Date
import kotlin.time.Duration.Companion.days
import kotlin.time.Duration.Companion.milliseconds
@Composable
@Stable
@ -83,4 +86,29 @@ fun Long?.toLocalDateString(): String {
val date = Date(this)
val format = SimpleDateFormat("yyyy-MM-dd", java.util.Locale.getDefault())
return format.format(date)
}
// 未来相对时间如“3天后”的实现最小层级为天
fun Long?.toRelativeTimeString(): String {
if (this == null) return ""
val today = Calendar.getInstance().apply {
// 将时间设置为当天的开始00:00:00.000
// 兼容API24
set(Calendar.HOUR_OF_DAY, 0)
set(Calendar.MINUTE, 0)
set(Calendar.SECOND, 0)
set(Calendar.MILLISECOND, 0)
}.timeInMillis
val diff = (this - today).milliseconds
return diff.toComponents { days, _, _, _, _ ->
when {
days.days == 1.days -> "明天"
days.days > 1.days && days.days < 7.days -> "${days}天后"
days.days in 7.days..<30.days -> "${days / 7}周后"
days.days in 30.days..<365.days -> "${days / 30}个月后"
days.days >= 365.days -> "${days / 365}年后"
else -> "今天"
}
}
}