feat(shift): long-press flips and rephases subsequent days

长按语义从"设相位断点"改为"翻转该天并从次日起重排":
- 翻转当天 override + 次日插入 PhaseBreak(重排起点)
- 后续按 cycle 自动顺延,匹配用户直觉
- 再次长按同一天可撤销(移除 override + 关联断点)
- 角标"断"改为"起",文案同步更新
This commit is contained in:
xfy 2026-07-09 16:14:41 +08:00
parent 14a3239b39
commit 66a054f2d8
2 changed files with 42 additions and 17 deletions

View File

@ -48,10 +48,11 @@ import plus.rua.project.ShiftKind
import plus.rua.project.ShiftPattern
/**
* 班次设置页用的迷你月历点某天翻转班/,长按设/清相位断点
* 班次设置页用的迷你月历点某天翻转班/(仅当天),长按翻转并从次日起重排周期
*
* 月份可前后翻页,每格显示日期数字与班次角标(//)
* 点击翻转该天的班/ override;长按切换相位断点
* 月份可前后翻页,每格显示日期数字与班次角标(//)
* 点击翻转该天的班/ override;长按翻转该天并在次日插入重排起点,
* 后续按 cycle 重新顺延再次长按同一天可撤销
*
* @param pattern 当前轮班配置(只读), [onPatternChange] 修改
* @param onPatternChange 修改后的新 pattern 回调
@ -125,7 +126,7 @@ fun ShiftCalendarGrid(
date = date,
pattern = pattern,
onClick = { onPatternChange(toggleOverride(pattern, date)) },
onLongClick = { onPatternChange(togglePhaseBreak(pattern, date)) },
onLongClick = { onPatternChange(toggleFlipAndRephase(pattern, date)) },
modifier = Modifier.weight(1f)
)
} else {
@ -152,15 +153,38 @@ private fun toggleOverride(pattern: ShiftPattern, date: LocalDate): ShiftPattern
}
/**
* 切换某天的相位断点:已有则清除,没有则新增(cycleOffset=0)
* 翻转某天的班/,并从次日起重排周期(后续按 cycle 重新顺延)
*
* 实现:翻转该天 override + [date] 次日插入 PhaseBreak(offset=0)
* 次日成为新的 cycle[0],后续自动按周期顺延
*
* 撤销:再次长按同一天,若存在由该天派生的断点(次日offset=0),则移除 override 与该断点
*
* @param date 被翻转并作为重排起点的日期
*/
private fun togglePhaseBreak(pattern: ShiftPattern, date: LocalDate): ShiftPattern {
val existing = pattern.phaseBreaks.find { it.date == date }
return if (existing != null) {
pattern.copy(phaseBreaks = pattern.phaseBreaks - existing)
} else {
pattern.copy(phaseBreaks = pattern.phaseBreaks + PhaseBreak(date, 0))
private fun toggleFlipAndRephase(pattern: ShiftPattern, date: LocalDate): ShiftPattern {
val nextDay = date.plus(DatePeriod(days = 1))
val derivedBreak = pattern.phaseBreaks.find { it.date == nextDay && it.cycleOffset == 0 }
if (derivedBreak != null) {
// 撤销:移除该天的 override 与关联断点
return pattern.copy(
overrides = pattern.overrides - date,
phaseBreaks = pattern.phaseBreaks - derivedBreak
)
}
// 翻转该天 + 次日插入断点重排
val current = pattern.kindAt(date) ?: return pattern
val newVal = if (current == ShiftKind.WORK) ShiftKind.OFF else ShiftKind.WORK
val tempPattern = pattern.copy(overrides = pattern.overrides - date)
val base = tempPattern.kindAt(date)
val newOverrides = if (newVal == base) pattern.overrides - date
else pattern.overrides + (date to newVal)
return pattern.copy(
overrides = newOverrides,
phaseBreaks = pattern.phaseBreaks + PhaseBreak(nextDay, 0)
)
}
/**
@ -182,16 +206,17 @@ private fun ShiftDayCell(
modifier: Modifier = Modifier
) {
val kind = pattern.kindAt(date)
val isBreak = pattern.phaseBreaks.any { it.date == date }
// 断点在"翻转日的次日",表示该天为重排起点
val isRephaseStart = pattern.phaseBreaks.any { it.date == date && it.cycleOffset == 0 }
val badgeColor = when {
isBreak -> MaterialTheme.colorScheme.tertiary
isRephaseStart -> MaterialTheme.colorScheme.tertiary
kind == ShiftKind.WORK -> MaterialTheme.colorScheme.primary
kind == ShiftKind.OFF -> MaterialTheme.colorScheme.error
else -> Color.Transparent
}
val badgeText = when {
isBreak -> ""
isRephaseStart -> ""
kind == ShiftKind.WORK -> ""
kind == ShiftKind.OFF -> ""
else -> ""

View File

@ -59,7 +59,7 @@ import kotlin.time.Instant
* 页面结构三段:
* 1. 基础周期:锚点日期当前周期展示;
* 2. 预设方案:1班1休 / 2班2休 / 3班3休 / 4班4休 快捷切换;
* 3. 调班设置:迷你月历(=翻转班/,长按=/清相位断点)+ 恢复默认
* 3. 调班设置:迷你月历(=翻转班/,长按=翻转并从次日起重排)+ 恢复默认
*
* @param onBack 返回回调( Activity 触发 finishWithSlideBack)
*/
@ -156,12 +156,12 @@ fun ShiftPatternScreen(onBack: () -> Unit) {
Text("调班设置", style = MaterialTheme.typography.titleSmall, fontWeight = FontWeight.SemiBold)
ShiftCalendarGrid(pattern = pattern, onPatternChange = { pattern = it })
Text(
text = "点某天 = 翻转班/休,长按某天 = 设/清相位断点",
text = "点某天 = 翻转班/休(仅当天),长按某天 = 翻转并从次日起重排",
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)
Text(
text = "图例:班(蓝)/休(红)/点(琥珀)",
text = "图例:班(蓝)/休(红)/重排起点(琥珀)",
style = MaterialTheme.typography.labelSmall,
color = MaterialTheme.colorScheme.onSurfaceVariant
)