From ecf4cf601ea59dac802a22e4c63aeb60d41d4637 Mon Sep 17 00:00:00 2001 From: meyou <2636699780@qq.com> Date: Sat, 16 May 2026 19:12:18 +0800 Subject: [PATCH] =?UTF-8?q?=E6=96=B0=E5=A2=9E=E4=B8=AA=E4=BA=BA=E8=BD=AE?= =?UTF-8?q?=E7=8F=AD=20MVP:=E5=B7=A6=E4=B8=8A=E8=A7=92=E8=83=B6=E5=9B=8A?= =?UTF-8?q?=E6=98=BE=E7=A4=BA=E7=8F=AD/=E4=BC=91?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增 ShiftPattern 数据模型,以锚点日期 + 循环序列描述周期性轮班,与法定调休完全独立。 默认配置 2026-05-15 起 [班,班,休,休] 4 天周期,DayCell 左上角渲染胶囊角标。 --- .../plus/rua/project/CalendarViewModel.kt | 13 ++++++++ .../kotlin/plus/rua/project/ShiftPattern.kt | 33 +++++++++++++++++++ .../plus/rua/project/ui/CalendarMonthPage.kt | 3 ++ .../plus/rua/project/ui/CalendarMonthView.kt | 2 ++ .../plus/rua/project/ui/CalendarPager.kt | 3 ++ .../kotlin/plus/rua/project/ui/DayCell.kt | 30 +++++++++++++++++ .../kotlin/plus/rua/project/ui/WeekPager.kt | 3 ++ 7 files changed, 87 insertions(+) create mode 100644 shared/src/commonMain/kotlin/plus/rua/project/ShiftPattern.kt diff --git a/shared/src/commonMain/kotlin/plus/rua/project/CalendarViewModel.kt b/shared/src/commonMain/kotlin/plus/rua/project/CalendarViewModel.kt index b468972..d81438a 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/CalendarViewModel.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/CalendarViewModel.kt @@ -77,6 +77,19 @@ class CalendarViewModel( var yearViewYear by mutableStateOf(today.year) internal set + /** + * 个人轮班。与法定节假日完全独立,不受调休影响。 + * MVP 默认:2026-05-15 起,2 班 2 休循环。后续接入设置页与持久化。 + */ + var shiftPattern: ShiftPattern? by mutableStateOf( + ShiftPattern( + anchorDate = LocalDate(2026, 5, 15), + cycle = listOf(ShiftKind.WORK, ShiftKind.WORK, ShiftKind.OFF, ShiftKind.OFF) + ) + ) + + fun shiftKindAt(date: LocalDate): ShiftKind? = shiftPattern?.kindAt(date) + /** * 选中指定日期。 * diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ShiftPattern.kt b/shared/src/commonMain/kotlin/plus/rua/project/ShiftPattern.kt new file mode 100644 index 0000000..d34037f --- /dev/null +++ b/shared/src/commonMain/kotlin/plus/rua/project/ShiftPattern.kt @@ -0,0 +1,33 @@ +package plus.rua.project + +import kotlinx.datetime.LocalDate +import kotlinx.datetime.daysUntil + +/** + * 个人轮班类型。仅区分上班与休息;后续可扩展早/中/晚班、休假等。 + */ +enum class ShiftKind { WORK, OFF } + +/** + * 个人轮班周期。 + * + * 与法定节假日完全独立:周期内某天是 WORK 还是 OFF,只看 + * `(date - anchorDate) mod cycle.size` 在 cycle 中的取值,不受任何节假日/调休影响。 + * + * @param anchorDate 周期基准日,对应 cycle[0] + * @param cycle 一个周期内的班次序列,例如 [WORK, WORK, OFF, OFF] 表示 "2 班 2 休" + * @param name 方案名,用于后续多套方案场景 + */ +data class ShiftPattern( + val anchorDate: LocalDate, + val cycle: List, + val name: String = "默认" +) { + fun kindAt(date: LocalDate): ShiftKind? { + if (cycle.isEmpty()) return null + val diff = anchorDate.daysUntil(date) + val size = cycle.size + val idx = ((diff % size) + size) % size + return cycle[idx] + } +} diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthPage.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthPage.kt index bcbbc5a..1ecd531 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthPage.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthPage.kt @@ -22,6 +22,7 @@ import kotlinx.datetime.LocalDate import kotlinx.datetime.minus import kotlinx.datetime.number import kotlinx.datetime.plus +import plus.rua.project.ShiftKind /** * 月度日历网格页面,支持两阶段折叠动画。 @@ -50,6 +51,7 @@ fun CalendarMonthPage( collapseProgress: Float, rowHeightPx: Int, effectiveWeeks: Float, + shiftKindAt: (LocalDate) -> ShiftKind?, onRowHeightMeasured: ((Int) -> Unit)? = null, modifier: Modifier = Modifier ) { @@ -152,6 +154,7 @@ fun CalendarMonthPage( isCurrentMonth = dayData.isCurrentMonth, isSelected = dayData.date == selectedDate, isToday = dayData.date == today, + shiftKind = shiftKindAt(dayData.date), onClick = { onDateClick(dayData.date) }, modifier = Modifier.weight(1f) ) diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt index b7c955b..787bb0f 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarMonthView.kt @@ -259,6 +259,7 @@ fun CalendarMonthView( } viewModel.selectDate(date) }, + shiftKindAt = { date -> viewModel.shiftKindAt(date) }, modifier = pagerModifier ) } else { @@ -275,6 +276,7 @@ fun CalendarMonthView( collapseProgress = viewModel.collapseProgress, rowHeightPx = rowHeightPx, effectiveWeeks = effectiveWeeks, + shiftKindAt = { date -> viewModel.shiftKindAt(date) }, onRowHeightMeasured = { h -> if (h > 0) rowHeightPx = h }, diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarPager.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarPager.kt index 1d29e21..def21e0 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarPager.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/CalendarPager.kt @@ -14,6 +14,7 @@ import kotlinx.coroutines.flow.drop import kotlinx.coroutines.launch import kotlinx.datetime.LocalDate import kotlinx.datetime.number +import plus.rua.project.ShiftKind import kotlin.math.abs /** @@ -42,6 +43,7 @@ fun CalendarPager( collapseProgress: Float, rowHeightPx: Int, effectiveWeeks: Float, + shiftKindAt: (LocalDate) -> ShiftKind?, onRowHeightMeasured: ((Int) -> Unit)? = null, pagerState: PagerState, modifier: Modifier = Modifier @@ -94,6 +96,7 @@ fun CalendarPager( collapseProgress = collapseProgress, rowHeightPx = rowHeightPx, effectiveWeeks = effectiveWeeks, + shiftKindAt = shiftKindAt, onRowHeightMeasured = onRowHeightMeasured, modifier = Modifier.alpha(alpha) ) diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/DayCell.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/DayCell.kt index 4ebf09a..f95f690 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/DayCell.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/DayCell.kt @@ -36,6 +36,7 @@ import androidx.compose.ui.unit.sp import androidx.compose.ui.zIndex import com.tyme.solar.SolarDay import kotlinx.datetime.LocalDate +import plus.rua.project.ShiftKind enum class DayCellState { NORMAL, OTHER_MONTH, TODAY, SELECTED, SELECTED_TODAY @@ -48,6 +49,7 @@ enum class DayCellState { * @param isCurrentMonth 是否属于当前显示月份 * @param isSelected 是否为选中日期 * @param isToday 是否为今天 + * @param shiftKind 个人轮班类型,左上角胶囊显示;null 表示不显示。与法定调休完全独立。 * @param onClick 点击回调 * @param modifier 外部布局修饰符 */ @@ -57,6 +59,7 @@ fun DayCell( isCurrentMonth: Boolean, isSelected: Boolean, isToday: Boolean, + shiftKind: ShiftKind?, onClick: () -> Unit, modifier: Modifier = Modifier ) { @@ -243,6 +246,33 @@ fun DayCell( ) } } + if (shiftKind != null) { + val shiftBgColor = if (shiftKind == ShiftKind.WORK) { + MaterialTheme.colorScheme.primary + } else { + MaterialTheme.colorScheme.error + } + val shiftFgColor = if (shiftKind == ShiftKind.WORK) { + MaterialTheme.colorScheme.onPrimary + } else { + MaterialTheme.colorScheme.onError + } + val shiftLabel = if (shiftKind == ShiftKind.WORK) "班" else "休" + val shiftAlpha = if (isCurrentMonth) 1f else 0.38f + Text( + text = shiftLabel, + color = shiftFgColor.copy(alpha = shiftAlpha), + fontSize = 9.sp, + fontWeight = FontWeight.Bold, + lineHeight = 9.sp, + modifier = Modifier + .align(Alignment.TopStart) + .zIndex(1f) + .padding(top = 1.dp, start = 2.dp) + .background(shiftBgColor.copy(alpha = shiftAlpha), CircleShape) + .padding(horizontal = 2.dp) + ) + } if (holidayBadge != null) { Text( text = holidayBadge, diff --git a/shared/src/commonMain/kotlin/plus/rua/project/ui/WeekPager.kt b/shared/src/commonMain/kotlin/plus/rua/project/ui/WeekPager.kt index 45ba90b..338ab2b 100644 --- a/shared/src/commonMain/kotlin/plus/rua/project/ui/WeekPager.kt +++ b/shared/src/commonMain/kotlin/plus/rua/project/ui/WeekPager.kt @@ -18,6 +18,7 @@ import kotlinx.datetime.DatePeriod import kotlinx.datetime.LocalDate import kotlinx.datetime.daysUntil import kotlinx.datetime.plus +import plus.rua.project.ShiftKind import kotlin.math.abs /** @@ -35,6 +36,7 @@ fun WeekPager( today: LocalDate, onDateClick: (LocalDate) -> Unit, onWeekChanged: (LocalDate) -> Unit, + shiftKindAt: (LocalDate) -> ShiftKind?, modifier: Modifier = Modifier ) { val initialWeekMonday = remember { selectedDate.toWeekMonday() } @@ -82,6 +84,7 @@ fun WeekPager( && date.year == selectedDate.year, isSelected = date == selectedDate, isToday = date == today, + shiftKind = shiftKindAt(date), onClick = { onDateClick(date) }, modifier = Modifier.weight(1f) )