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 import plus.rua.project.ShiftPattern
/** /**
* 班次设置页用的迷你月历点某天翻转班/,长按设/清相位断点 * 班次设置页用的迷你月历点某天翻转班/(仅当天),长按翻转并从次日起重排周期
* *
* 月份可前后翻页,每格显示日期数字与班次角标(//) * 月份可前后翻页,每格显示日期数字与班次角标(//)
* 点击翻转该天的班/ override;长按切换相位断点 * 点击翻转该天的班/ override;长按翻转该天并在次日插入重排起点,
* 后续按 cycle 重新顺延再次长按同一天可撤销
* *
* @param pattern 当前轮班配置(只读), [onPatternChange] 修改 * @param pattern 当前轮班配置(只读), [onPatternChange] 修改
* @param onPatternChange 修改后的新 pattern 回调 * @param onPatternChange 修改后的新 pattern 回调
@ -125,7 +126,7 @@ fun ShiftCalendarGrid(
date = date, date = date,
pattern = pattern, pattern = pattern,
onClick = { onPatternChange(toggleOverride(pattern, date)) }, onClick = { onPatternChange(toggleOverride(pattern, date)) },
onLongClick = { onPatternChange(togglePhaseBreak(pattern, date)) }, onLongClick = { onPatternChange(toggleFlipAndRephase(pattern, date)) },
modifier = Modifier.weight(1f) modifier = Modifier.weight(1f)
) )
} else { } 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 { private fun toggleFlipAndRephase(pattern: ShiftPattern, date: LocalDate): ShiftPattern {
val existing = pattern.phaseBreaks.find { it.date == date } val nextDay = date.plus(DatePeriod(days = 1))
return if (existing != null) { val derivedBreak = pattern.phaseBreaks.find { it.date == nextDay && it.cycleOffset == 0 }
pattern.copy(phaseBreaks = pattern.phaseBreaks - existing)
} else { if (derivedBreak != null) {
pattern.copy(phaseBreaks = pattern.phaseBreaks + PhaseBreak(date, 0)) // 撤销:移除该天的 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 modifier: Modifier = Modifier
) { ) {
val kind = pattern.kindAt(date) 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 { val badgeColor = when {
isBreak -> MaterialTheme.colorScheme.tertiary isRephaseStart -> MaterialTheme.colorScheme.tertiary
kind == ShiftKind.WORK -> MaterialTheme.colorScheme.primary kind == ShiftKind.WORK -> MaterialTheme.colorScheme.primary
kind == ShiftKind.OFF -> MaterialTheme.colorScheme.error kind == ShiftKind.OFF -> MaterialTheme.colorScheme.error
else -> Color.Transparent else -> Color.Transparent
} }
val badgeText = when { val badgeText = when {
isBreak -> "" isRephaseStart -> ""
kind == ShiftKind.WORK -> "" kind == ShiftKind.WORK -> ""
kind == ShiftKind.OFF -> "" kind == ShiftKind.OFF -> ""
else -> "" else -> ""

View File

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