feat: 底部卡片展示选中日期的相对天数、公历与农历信息

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
xfy 2026-05-19 11:11:53 +08:00
parent 39bb2301d3
commit 0d58be45bc
3 changed files with 100 additions and 2 deletions

View File

@ -2,27 +2,42 @@ package plus.rua.project.ui
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.gestures.detectVerticalDragGestures import androidx.compose.foundation.gestures.detectVerticalDragGestures
import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Surface import androidx.compose.material3.Surface
import androidx.compose.material3.Text
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip import androidx.compose.ui.draw.clip
import androidx.compose.ui.input.pointer.pointerInput import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.LocalDensity import androidx.compose.ui.platform.LocalDensity
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import kotlinx.datetime.LocalDate
import plus.rua.project.CalendarViewModel import plus.rua.project.CalendarViewModel
/** /**
* 底部卡片折叠状态下支持垂直拖拽触发折叠动画 * 底部卡片折叠状态下支持垂直拖拽触发折叠动画
* *
* 卡片顶部显示拖拽把手下方展示选中日期信息
* 左侧为相对今天的天数描述A和公历日期B
* 右侧为农历日期C
*
* @param viewModel 日历 ViewModel用于读取折叠状态和驱动拖拽 * @param viewModel 日历 ViewModel用于读取折叠状态和驱动拖拽
* @param selectedDate 当前选中的日期
* @param today 今天的日期
* @param dragRangePx 拖拽手势映射范围像素progress 01 对应手指移动此距离 * @param dragRangePx 拖拽手势映射范围像素progress 01 对应手指移动此距离
* 应设为折叠时日历实际高度变化量 (weeks-1)×rowHeight使拖拽跟手 * 应设为折叠时日历实际高度变化量 (weeks-1)×rowHeight使拖拽跟手
* @param modifier 外部布局修饰符 * @param modifier 外部布局修饰符
@ -30,10 +45,16 @@ import plus.rua.project.CalendarViewModel
@Composable @Composable
fun BottomCard( fun BottomCard(
viewModel: CalendarViewModel, viewModel: CalendarViewModel,
selectedDate: LocalDate,
today: LocalDate,
dragRangePx: Float, dragRangePx: Float,
modifier: Modifier = Modifier modifier: Modifier = Modifier
) { ) {
val density = LocalDensity.current val density = LocalDensity.current
val relativeDesc = relativeDayDescription(selectedDate, today)
@Suppress("DEPRECATION") // monthNumber 无替代 APIkotlinx-datetime 尚未提供新接口
val solarDesc = "${selectedDate.monthNumber}${selectedDate.day}"
val lunarDesc = formatLunarDate(selectedDate)
Surface( Surface(
modifier = modifier modifier = modifier
@ -80,16 +101,52 @@ fun BottomCard(
color = MaterialTheme.colorScheme.surfaceVariant, color = MaterialTheme.colorScheme.surfaceVariant,
shadowElevation = 4.dp shadowElevation = 4.dp
) { ) {
Box(modifier = Modifier.fillMaxSize()) { Column(modifier = Modifier.fillMaxSize()) {
// 拖拽把手
Box( Box(
modifier = Modifier modifier = Modifier
.align(Alignment.TopCenter)
.padding(top = 8.dp, bottom = 8.dp) .padding(top = 8.dp, bottom = 8.dp)
.clip(RoundedCornerShape(2.dp)) .clip(RoundedCornerShape(2.dp))
.background(MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.4f)) .background(MaterialTheme.colorScheme.onSurfaceVariant.copy(alpha = 0.4f))
.fillMaxWidth(0.15f) .fillMaxWidth(0.15f)
.height(4.dp) .height(4.dp)
.align(Alignment.CenterHorizontally)
) )
Spacer(modifier = Modifier.height(8.dp))
// A / B / C 信息行
Row(
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = 20.dp),
horizontalArrangement = Arrangement.SpaceBetween,
verticalAlignment = Alignment.CenterVertically
) {
// 左侧A相对天数和 B公历日期在同一行
Row(
verticalAlignment = Alignment.CenterVertically
) {
Text(
text = relativeDesc,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
fontSize = 14.sp
)
Spacer(modifier = Modifier.width(6.dp))
Text(
text = solarDesc,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
fontSize = 14.sp
)
}
// 右侧C农历日期
Text(
text = lunarDesc,
style = MaterialTheme.typography.bodyMedium,
color = MaterialTheme.colorScheme.onSurfaceVariant,
fontSize = 14.sp
)
}
} }
} }
} }

View File

@ -311,6 +311,8 @@ fun CalendarMonthView(
if (cardHeightPx > 0) { if (cardHeightPx > 0) {
BottomCard( BottomCard(
viewModel = viewModel, viewModel = viewModel,
selectedDate = viewModel.selectedDate,
today = today,
dragRangePx = dragRangePx, dragRangePx = dragRangePx,
modifier = Modifier modifier = Modifier
.fillMaxWidth() .fillMaxWidth()

View File

@ -1,7 +1,9 @@
package plus.rua.project.ui package plus.rua.project.ui
import com.tyme.solar.SolarDay
import kotlinx.datetime.DatePeriod import kotlinx.datetime.DatePeriod
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate
import kotlinx.datetime.daysUntil
import kotlinx.datetime.minus import kotlinx.datetime.minus
import kotlinx.datetime.number import kotlinx.datetime.number
import kotlinx.datetime.plus import kotlinx.datetime.plus
@ -130,3 +132,40 @@ fun pageToWeekMonday(page: Int, initial: LocalDate): LocalDate {
val offset = page - START_PAGE val offset = page - START_PAGE
return initial.plus(DatePeriod(days = offset * 7)) return initial.plus(DatePeriod(days = offset * 7))
} }
/**
* 计算选中日期相对于今天的天数描述
*
* 例如今天 19 选中 18 日返回"昨天"17 日返回"2天前"
* 20 日返回"明天"21 日返回"2天后"选中当天返回"今天"
*
* @param selectedDate 选中日期
* @param today 今天日期
* @return 相对天数描述
*/
fun relativeDayDescription(selectedDate: LocalDate, today: LocalDate): String {
val diff = today.daysUntil(selectedDate)
return when {
diff == 0 -> "今天"
diff == -1 -> "昨天"
diff == 1 -> "明天"
diff < 0 -> "${-diff}天前"
else -> "${diff}天后"
}
}
/**
* 将公历日期格式化为农历日期字符串
*
* 格式为"农历{月}{日}"例如"农历四月初三"
*
* @param date 公历日期
* @return 农历日期描述
*/
@Suppress("DEPRECATION") // monthNumber 无替代 APIkotlinx-datetime 尚未提供新接口
fun formatLunarDate(date: LocalDate): String {
val solarDay = SolarDay.fromYmd(date.year, date.monthNumber, date.day)
val lunarDay = solarDay.getLunarDay()
val lunarMonth = lunarDay.getLunarMonth()
return "农历${lunarMonth.getName()}${lunarDay.getName()}"
}