feat(chart): 用量详情页显示服务标识;图例只显示当前时间范围内模型(含 tooltip 残留修复)

This commit is contained in:
WaterRain 2026-09-11 06:52:36 +00:00
parent eecf54986f
commit a079a0bfcb
No known key found for this signature in database
3 changed files with 31 additions and 6 deletions

View File

@ -551,7 +551,8 @@ internal fun StackedBarChart(
Text(stringResource(R.string.common_no_data), color = InkMuted, style = MaterialTheme.typography.bodySmall) Text(stringResource(R.string.common_no_data), color = InkMuted, style = MaterialTheme.typography.bodySmall)
return return
} }
var tooltipBucket by remember { mutableStateOf(buckets.lastOrNull()) } // key = buckets:切换时间范围/筛选后重置 tooltip,避免残留旧数据
var tooltipBucket by remember(buckets) { mutableStateOf(buckets.lastOrNull()) }
val density = LocalDensity.current val density = LocalDensity.current
val d = density.density val d = density.density
val barCount = buckets.size val barCount = buckets.size
@ -681,7 +682,8 @@ internal fun LineChart(
Text(stringResource(R.string.common_no_data), color = InkMuted, style = MaterialTheme.typography.bodySmall) Text(stringResource(R.string.common_no_data), color = InkMuted, style = MaterialTheme.typography.bodySmall)
return return
} }
var tooltipBucket by remember { mutableStateOf(buckets.lastOrNull()) } // key = buckets:切换时间范围/筛选后重置 tooltip,避免残留旧数据
var tooltipBucket by remember(buckets) { mutableStateOf(buckets.lastOrNull()) }
val density = LocalDensity.current val density = LocalDensity.current
val d = density.density val d = density.density
val barCount = buckets.size val barCount = buckets.size

View File

@ -153,10 +153,14 @@ class UsageChartViewModel @Inject constructor(
// 降级后或用户手动选择 → 关闭降级,正常显示 // 降级后或用户手动选择 → 关闭降级,正常显示
allowFallback = false allowFallback = false
// 图例数据源:只保留当前显示范围内实际出现过的模型
val rangeModels = result.flatMap { it.byModel.keys }.distinct().sorted()
_state.update { _state.update {
it.copy( it.copy(
buckets = result, buckets = result,
allModels = allModels, allModels = allModels,
rangeModels = rangeModels,
loading = false loading = false
) )
} }
@ -262,6 +266,8 @@ data class ChartUiState(
val loading: Boolean = true, val loading: Boolean = true,
val granularity: ChartGranularity = ChartGranularity.LAST_5H_HOURLY, val granularity: ChartGranularity = ChartGranularity.LAST_5H_HOURLY,
val allModels: List<String> = emptyList(), val allModels: List<String> = emptyList(),
/** 当前显示范围内实际出现过的模型(图例只显示这些) */
val rangeModels: List<String> = emptyList(),
val selectedModels: Set<String> = emptySet(), // empty = all val selectedModels: Set<String> = emptySet(), // empty = all
val buckets: List<ChartBucket> = emptyList(), val buckets: List<ChartBucket> = emptyList(),
val useUtc8: Boolean = false, val useUtc8: Boolean = false,

View File

@ -53,6 +53,7 @@ import androidx.compose.ui.graphics.Color
import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.res.stringResource import androidx.compose.ui.res.stringResource
import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp import androidx.compose.ui.unit.sp
import androidx.hilt.navigation.compose.hiltViewModel import androidx.hilt.navigation.compose.hiltViewModel
@ -110,7 +111,23 @@ fun UsageDetailScreen(
snackbarHost = { SnackbarHost(snackbarHostState) }, snackbarHost = { SnackbarHost(snackbarHostState) },
topBar = { topBar = {
TopAppBar( TopAppBar(
title = { Text(stringResource(R.string.title_usage_detail)) }, title = {
val serviceLabel = if (clearViewModel == null) "OCGO" else "CommandCode"
Column {
Text(
stringResource(R.string.title_usage_detail),
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
Text(
serviceLabel,
style = MaterialTheme.typography.bodySmall,
color = InkMuted,
maxLines = 1,
overflow = TextOverflow.Ellipsis
)
}
},
navigationIcon = { IconButton(onClick = onBack) { Icon(Icons.Filled.ArrowBack, stringResource(R.string.action_back)) } }, navigationIcon = { IconButton(onClick = onBack) { Icon(Icons.Filled.ArrowBack, stringResource(R.string.action_back)) } },
actions = { actions = {
if (clearViewModel != null) { if (clearViewModel != null) {
@ -209,7 +226,7 @@ fun UsageDetailScreen(
if (wideEnough) { if (wideEnough) {
// 面板够宽:前两张图表并排 // 面板够宽:前两张图表并排
item { item {
val models = state.selectedModels.ifEmpty { state.allModels.toSet() } val models = state.rangeModels
val costTotal = state.buckets.sumOf { it.totalCost.toDouble() / 100_000_000.0 } val costTotal = state.buckets.sumOf { it.totalCost.toDouble() / 100_000_000.0 }
val reqTotal = state.buckets.sumOf { it.totalRequests } val reqTotal = state.buckets.sumOf { it.totalRequests }
Row( Row(
@ -256,7 +273,7 @@ fun UsageDetailScreen(
} else { } else {
// 窄面板:三张图表纵向堆叠 // 窄面板:三张图表纵向堆叠
item { item {
val models = state.selectedModels.ifEmpty { state.allModels.toSet() } val models = state.rangeModels
val costTotal = state.buckets.sumOf { it.totalCost.toDouble() / 100_000_000.0 } val costTotal = state.buckets.sumOf { it.totalCost.toDouble() / 100_000_000.0 }
ChartCard(stringResource(R.string.chart_cost), "$${String.format(Locale.US, "%.4f", costTotal)}", { showCostDetail = true }) { ChartCard(stringResource(R.string.chart_cost), "$${String.format(Locale.US, "%.4f", costTotal)}", { showCostDetail = true }) {
StackedBarChart(state.buckets, StackedBarChart(state.buckets,
@ -331,7 +348,7 @@ fun UsageDetailScreen(
minDate = startDate minDate = startDate
) )
} }
val models = state.selectedModels.ifEmpty { state.allModels.toSet() } val models = state.rangeModels
if (showCostDetail) ChartDetailDialog(stringResource(R.string.chart_detail_cost), { showCostDetail = false }) { if (showCostDetail) ChartDetailDialog(stringResource(R.string.chart_detail_cost), { showCostDetail = false }) {
models.forEach { model -> val t = state.buckets.sumOf { it.byModel[model]?.cost ?:0L }; if (t>0) DetailRow(model, "$${String.format(Locale.US, "%.4f", t/100_000_000.0)}") } models.forEach { model -> val t = state.buckets.sumOf { it.byModel[model]?.cost ?:0L }; if (t>0) DetailRow(model, "$${String.format(Locale.US, "%.4f", t/100_000_000.0)}") }
} }