feat(shift): tap year-month title to pick date in mini calendar

点击迷你月历顶部"年 月"标题弹出 DatePicker,
选定日期后跳转到对应月份,比连点箭头快。
This commit is contained in:
xfy 2026-07-09 16:55:22 +08:00
parent 62ed8bd09a
commit 1cf950817e

View File

@ -2,6 +2,7 @@ package plus.rua.project.ui
import androidx.compose.foundation.ExperimentalFoundationApi import androidx.compose.foundation.ExperimentalFoundationApi
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.combinedClickable import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.Arrangement import androidx.compose.foundation.layout.Arrangement
import androidx.compose.foundation.layout.Box import androidx.compose.foundation.layout.Box
@ -17,10 +18,15 @@ import androidx.compose.material.icons.filled.ChevronLeft
import androidx.compose.material.icons.filled.ChevronRight import androidx.compose.material.icons.filled.ChevronRight
import androidx.compose.material3.Card import androidx.compose.material3.Card
import androidx.compose.material3.CardDefaults import androidx.compose.material3.CardDefaults
import androidx.compose.material3.DatePicker
import androidx.compose.material3.DatePickerDialog
import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.Icon import androidx.compose.material3.Icon
import androidx.compose.material3.IconButton import androidx.compose.material3.IconButton
import androidx.compose.material3.MaterialTheme import androidx.compose.material3.MaterialTheme
import androidx.compose.material3.Text import androidx.compose.material3.Text
import androidx.compose.material3.TextButton
import androidx.compose.material3.rememberDatePickerState
import androidx.compose.runtime.Composable import androidx.compose.runtime.Composable
import androidx.compose.runtime.getValue import androidx.compose.runtime.getValue
import androidx.compose.runtime.mutableStateOf import androidx.compose.runtime.mutableStateOf
@ -37,12 +43,15 @@ import kotlinx.datetime.DatePeriod
import kotlinx.datetime.LocalDate import kotlinx.datetime.LocalDate
import kotlinx.datetime.Month import kotlinx.datetime.Month
import kotlinx.datetime.TimeZone import kotlinx.datetime.TimeZone
import kotlinx.datetime.atStartOfDayIn
import kotlinx.datetime.isoDayNumber import kotlinx.datetime.isoDayNumber
import kotlinx.datetime.minus import kotlinx.datetime.minus
import kotlinx.datetime.number import kotlinx.datetime.number
import kotlinx.datetime.plus import kotlinx.datetime.plus
import kotlinx.datetime.toLocalDateTime
import kotlinx.datetime.todayIn import kotlinx.datetime.todayIn
import kotlin.time.Clock import kotlin.time.Clock
import kotlin.time.Instant
import plus.rua.project.RephaseFlip import plus.rua.project.RephaseFlip
import plus.rua.project.ShiftKind import plus.rua.project.ShiftKind
import plus.rua.project.ShiftPattern import plus.rua.project.ShiftPattern
@ -58,7 +67,7 @@ import plus.rua.project.ShiftPattern
* @param onPatternChange 修改后的新 pattern 回调 * @param onPatternChange 修改后的新 pattern 回调
* @param modifier 外部布局修饰符 * @param modifier 外部布局修饰符
*/ */
@OptIn(ExperimentalFoundationApi::class) @OptIn(ExperimentalFoundationApi::class, ExperimentalMaterial3Api::class)
@Composable @Composable
fun ShiftCalendarGrid( fun ShiftCalendarGrid(
pattern: ShiftPattern, pattern: ShiftPattern,
@ -68,6 +77,7 @@ fun ShiftCalendarGrid(
val today = remember { Clock.System.todayIn(TimeZone.currentSystemDefault()) } val today = remember { Clock.System.todayIn(TimeZone.currentSystemDefault()) }
var viewYear by remember { mutableStateOf(today.year) } var viewYear by remember { mutableStateOf(today.year) }
var viewMonth by remember { mutableStateOf(today.month.number) } var viewMonth by remember { mutableStateOf(today.month.number) }
var showMonthPicker by remember { mutableStateOf(false) }
// 复用主日历的网格计算(周一为首日,动态行数 4/5/6) // 复用主日历的网格计算(周一为首日,动态行数 4/5/6)
val gridInfo = remember(viewYear, viewMonth) { getMonthGridInfo(viewYear, viewMonth) } val gridInfo = remember(viewYear, viewMonth) { getMonthGridInfo(viewYear, viewMonth) }
@ -94,7 +104,8 @@ fun ShiftCalendarGrid(
Text( Text(
text = "${viewYear}${viewMonth}", text = "${viewYear}${viewMonth}",
style = MaterialTheme.typography.titleSmall, style = MaterialTheme.typography.titleSmall,
fontWeight = FontWeight.SemiBold fontWeight = FontWeight.SemiBold,
modifier = Modifier.clickable { showMonthPicker = true }
) )
IconButton(onClick = { IconButton(onClick = {
if (viewMonth == 12) { viewMonth = 1; viewYear += 1 } else viewMonth += 1 if (viewMonth == 12) { viewMonth = 1; viewYear += 1 } else viewMonth += 1
@ -138,6 +149,31 @@ fun ShiftCalendarGrid(
} }
} }
} }
if (showMonthPicker) {
val initialDate = LocalDate(viewYear, Month(viewMonth), 1)
val datePickerState = rememberDatePickerState(
initialSelectedDateMillis = initialDate.toEpochMillis()
)
DatePickerDialog(
onDismissRequest = { showMonthPicker = false },
confirmButton = {
TextButton(onClick = {
datePickerState.selectedDateMillis?.let { millis ->
val picked = millis.toLocalDate()
viewYear = picked.year
viewMonth = picked.month.number
}
showMonthPicker = false
}) { Text("确定") }
},
dismissButton = {
TextButton(onClick = { showMonthPicker = false }) { Text("取消") }
}
) {
DatePicker(state = datePickerState)
}
}
} }
/** /**
@ -260,3 +296,9 @@ private fun ShiftDayCell(
} }
} }
} }
private fun LocalDate.toEpochMillis(): Long =
this.atStartOfDayIn(TimeZone.UTC).toEpochMilliseconds()
private fun Long.toLocalDate(): LocalDate =
Instant.fromEpochMilliseconds(this).toLocalDateTime(TimeZone.UTC).date