feat: 法定假日改为背景色显示,支持连续边缘圆角
- 移除右上角 Text 角标,改用整格淡色背景("休"淡红、"班"淡蓝) - 连续假日自动边缘圆角:开头左边、结尾右边、中间无、单个四边 - 排班标记固定右上角,不再随 showLegalHoliday 切换位置 Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
b1185da27a
commit
e97909ec34
@ -9,7 +9,9 @@ import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.getValue
|
||||
import androidx.compose.runtime.key
|
||||
import androidx.compose.runtime.produceState
|
||||
import androidx.compose.runtime.remember
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clipToBounds
|
||||
@ -24,6 +26,7 @@ import kotlinx.datetime.LocalDate
|
||||
import kotlinx.datetime.minus
|
||||
import kotlinx.datetime.number
|
||||
import kotlinx.datetime.plus
|
||||
import plus.rua.project.LunarCache
|
||||
import plus.rua.project.ShiftKind
|
||||
|
||||
|
||||
@ -42,7 +45,7 @@ import plus.rua.project.ShiftKind
|
||||
* @param rowHeightPx 从外层传入的锁定行高(像素),折叠过程中不变
|
||||
* @param effectiveWeeks 当前有效行数(含翻页插值),用于计算总高度
|
||||
* @param shiftKindAt 日期 → 个人轮班类型的查询闭包
|
||||
* @param showLegalHoliday 是否显示法定调休角标。详见 [DayCell] 的同名参数。
|
||||
* @param showLegalHoliday 是否显示法定调休背景色。详见 [DayCell] 的同名参数。
|
||||
* @param onRowHeightMeasured 首次行高测量回调,外层据此锁定行高
|
||||
* @param modifier 外部布局修饰符
|
||||
*/
|
||||
@ -67,6 +70,34 @@ fun CalendarMonthPage(
|
||||
val density = LocalDensity.current
|
||||
val interactionSource = remember { MutableInteractionSource() }
|
||||
|
||||
val holidayBadges by produceState(
|
||||
initialValue = emptyMap<LocalDate, String?>(),
|
||||
key1 = days
|
||||
) {
|
||||
val map = mutableMapOf<LocalDate, String?>()
|
||||
for (dayData in days) {
|
||||
val info = LunarCache.default.getOrCompute(dayData.date)
|
||||
map[dayData.date] = info.holidayBadge
|
||||
}
|
||||
value = map
|
||||
}
|
||||
|
||||
val holidayEdges = remember(holidayBadges, days) {
|
||||
val map = mutableMapOf<LocalDate, HolidayEdgeInfo>()
|
||||
for (dayData in days) {
|
||||
val date = dayData.date
|
||||
val badge = holidayBadges[date]
|
||||
if (badge == null) continue
|
||||
val prevBadge = holidayBadges[date.minus(DatePeriod(days = 1))]
|
||||
val nextBadge = holidayBadges[date.plus(DatePeriod(days = 1))]
|
||||
map[date] = HolidayEdgeInfo(
|
||||
isStart = prevBadge != badge,
|
||||
isEnd = nextBadge != badge
|
||||
)
|
||||
}
|
||||
map
|
||||
}
|
||||
|
||||
val weeks = remember(days) { days.chunked(7) }
|
||||
val anchorIndex = remember(weeks, selectedDate) {
|
||||
weeks.indexOfFirst { week -> week.any { it.date == selectedDate } }
|
||||
@ -96,6 +127,7 @@ fun CalendarMonthPage(
|
||||
today = today,
|
||||
shiftKindAt = shiftKindAt,
|
||||
showLegalHoliday = showLegalHoliday,
|
||||
holidayEdges = holidayEdges,
|
||||
onDateClick = onDateClick,
|
||||
onRowHeightMeasured = onRowHeightMeasured,
|
||||
interactionSource = interactionSource
|
||||
@ -117,6 +149,7 @@ private fun WeekRow(
|
||||
today: LocalDate,
|
||||
shiftKindAt: (LocalDate) -> ShiftKind?,
|
||||
showLegalHoliday: Boolean,
|
||||
holidayEdges: Map<LocalDate, HolidayEdgeInfo>,
|
||||
onDateClick: (LocalDate) -> Unit,
|
||||
onRowHeightMeasured: ((Int) -> Unit)?,
|
||||
interactionSource: MutableInteractionSource,
|
||||
@ -197,6 +230,7 @@ private fun WeekRow(
|
||||
isToday = dayData.date == today,
|
||||
shiftKind = shiftKindAt(dayData.date),
|
||||
showLegalHoliday = showLegalHoliday,
|
||||
holidayEdgeInfo = holidayEdges[dayData.date],
|
||||
onClick = { onDateClick(dayData.date) },
|
||||
modifier = Modifier.weight(1f),
|
||||
interactionSource = interactionSource
|
||||
@ -212,6 +246,17 @@ private data class DayData(
|
||||
val isCurrentMonth: Boolean
|
||||
)
|
||||
|
||||
/**
|
||||
* 法定假日在连续序列中的边缘状态,用于决定背景圆角。
|
||||
*
|
||||
* @param isStart 是否为同类型连续假日的开始(前一天不是同类型)
|
||||
* @param isEnd 是否为同类型连续假日的结束(后一天不是同类型)
|
||||
*/
|
||||
data class HolidayEdgeInfo(
|
||||
val isStart: Boolean,
|
||||
val isEnd: Boolean
|
||||
)
|
||||
|
||||
@Suppress("DEPRECATION") // monthNumber 无替代 API,kotlinx-datetime 尚未提供新接口
|
||||
private fun generateMonthDays(year: Int, month: Int): List<DayData> {
|
||||
val firstOfMonth = LocalDate(year, month, 1)
|
||||
|
||||
@ -32,7 +32,7 @@ import kotlin.math.abs
|
||||
* @param rowHeightPx 锁定行高(像素)
|
||||
* @param effectiveWeeks 当前有效行数(含翻页插值)
|
||||
* @param shiftKindAt 日期 → 个人轮班类型的查询闭包
|
||||
* @param showLegalHoliday 是否显示法定调休角标。详见 [DayCell] 的同名参数。
|
||||
* @param showLegalHoliday 是否显示法定调休背景色。详见 [DayCell] 的同名参数。
|
||||
* @param onRowHeightMeasured 首次行高测量回调
|
||||
* @param pagerState 外层共享的 PagerState,用于保持翻页状态
|
||||
* @param modifier 外部布局修饰符
|
||||
|
||||
@ -12,8 +12,8 @@ import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.aspectRatio
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material3.MaterialTheme
|
||||
import androidx.compose.material3.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@ -52,9 +52,10 @@ enum class DayCellState {
|
||||
* @param isSelected 是否为选中日期
|
||||
* @param isToday 是否为今天
|
||||
* @param shiftKind 个人轮班类型;null 表示不显示。与法定调休完全独立。
|
||||
* @param showLegalHoliday 是否显示法定调休角标。
|
||||
* false(默认):排班放右上角,左上角空白,不显示法定调休。
|
||||
* true:排班放左上角,法定调休放右上角(旧版布局)。
|
||||
* @param showLegalHoliday 是否显示法定调休背景色。
|
||||
* false(默认):排班放右上角,不显示法定调休背景。
|
||||
* true:排班仍在右上角,法定假日以淡色背景显示("休"淡红,"班"淡蓝)。
|
||||
* @param holidayEdgeInfo 假日在连续序列中的边缘状态,决定背景圆角。null 表示无假日。
|
||||
* @param onClick 点击回调
|
||||
* @param modifier 外部布局修饰符
|
||||
*/
|
||||
@ -66,6 +67,7 @@ fun DayCell(
|
||||
isToday: Boolean,
|
||||
shiftKind: ShiftKind?,
|
||||
showLegalHoliday: Boolean,
|
||||
holidayEdgeInfo: HolidayEdgeInfo? = null,
|
||||
onClick: () -> Unit,
|
||||
modifier: Modifier = Modifier,
|
||||
interactionSource: MutableInteractionSource = remember { MutableInteractionSource() },
|
||||
@ -158,16 +160,30 @@ fun DayCell(
|
||||
label = "lunarColor"
|
||||
)
|
||||
|
||||
val holidayBadgeColor = when (holidayBadge) {
|
||||
"休" -> MaterialTheme.colorScheme.error
|
||||
"班" -> MaterialTheme.colorScheme.primary
|
||||
val holidayBgColor = when (holidayBadge) {
|
||||
"休" -> MaterialTheme.colorScheme.error.copy(alpha = 0.10f)
|
||||
"班" -> MaterialTheme.colorScheme.primary.copy(alpha = 0.08f)
|
||||
else -> Color.Transparent
|
||||
}
|
||||
val holidayBadgeAlpha = if (isCurrentMonth) 1f else 0.38f
|
||||
|
||||
Box(
|
||||
modifier = modifier.aspectRatio(1f)
|
||||
) {
|
||||
// 法定假日背景(最底层,与选中/今天状态叠加)
|
||||
if (showLegalHoliday && holidayBadge != null) {
|
||||
val holidayShape = when {
|
||||
holidayEdgeInfo?.isStart == true && holidayEdgeInfo.isEnd -> RoundedCornerShape(8.dp)
|
||||
holidayEdgeInfo?.isStart == true -> RoundedCornerShape(topStart = 8.dp, bottomStart = 8.dp)
|
||||
holidayEdgeInfo?.isEnd == true -> RoundedCornerShape(topEnd = 8.dp, bottomEnd = 8.dp)
|
||||
else -> RoundedCornerShape(0.dp)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
.padding(horizontal = 0.5.dp)
|
||||
.background(holidayBgColor, holidayShape)
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@ -231,51 +247,16 @@ fun DayCell(
|
||||
}
|
||||
val shiftLabel = if (shiftKind == ShiftKind.WORK) "班" else "休"
|
||||
val shiftAlpha = if (isCurrentMonth) 1f else 0.38f
|
||||
if (showLegalHoliday) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopStart)
|
||||
.zIndex(1f)
|
||||
.padding(top = 1.dp, start = 2.dp)
|
||||
.background(shiftAccentColor.copy(alpha = 0.12f), CircleShape)
|
||||
.size(16.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
text = shiftLabel,
|
||||
color = shiftAccentColor.copy(alpha = shiftAlpha),
|
||||
fontSize = 9.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
lineHeight = 9.sp
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Text(
|
||||
text = shiftLabel,
|
||||
color = shiftAccentColor.copy(alpha = shiftAlpha),
|
||||
fontSize = 9.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
lineHeight = 9.sp,
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.zIndex(1f)
|
||||
.padding(top = 1.dp, end = 2.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
if (showLegalHoliday && holidayBadge != null) {
|
||||
Text(
|
||||
text = holidayBadge,
|
||||
color = holidayBadgeColor.copy(alpha = holidayBadgeAlpha),
|
||||
text = shiftLabel,
|
||||
color = shiftAccentColor.copy(alpha = shiftAlpha),
|
||||
fontSize = 9.sp,
|
||||
fontWeight = FontWeight.Bold,
|
||||
lineHeight = 9.sp,
|
||||
modifier = Modifier
|
||||
.align(Alignment.TopEnd)
|
||||
.zIndex(1f)
|
||||
.background(MaterialTheme.colorScheme.background)
|
||||
.padding(top = 1.dp, end = 2.dp)
|
||||
.padding(horizontal = 2.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user