diff --git a/app/src/main/java/com/rainy/token/ui/dashboard/UsageChartScreen.kt b/app/src/main/java/com/rainy/token/ui/dashboard/UsageChartScreen.kt index f3170be..64f5a28 100644 --- a/app/src/main/java/com/rainy/token/ui/dashboard/UsageChartScreen.kt +++ b/app/src/main/java/com/rainy/token/ui/dashboard/UsageChartScreen.kt @@ -551,7 +551,8 @@ internal fun StackedBarChart( Text(stringResource(R.string.common_no_data), color = InkMuted, style = MaterialTheme.typography.bodySmall) return } - var tooltipBucket by remember { mutableStateOf(buckets.lastOrNull()) } + // key = buckets:切换时间范围/筛选后重置 tooltip,避免残留旧数据 + var tooltipBucket by remember(buckets) { mutableStateOf(buckets.lastOrNull()) } val density = LocalDensity.current val d = density.density val barCount = buckets.size @@ -681,7 +682,8 @@ internal fun LineChart( Text(stringResource(R.string.common_no_data), color = InkMuted, style = MaterialTheme.typography.bodySmall) return } - var tooltipBucket by remember { mutableStateOf(buckets.lastOrNull()) } + // key = buckets:切换时间范围/筛选后重置 tooltip,避免残留旧数据 + var tooltipBucket by remember(buckets) { mutableStateOf(buckets.lastOrNull()) } val density = LocalDensity.current val d = density.density val barCount = buckets.size diff --git a/app/src/main/java/com/rainy/token/ui/dashboard/UsageChartViewModel.kt b/app/src/main/java/com/rainy/token/ui/dashboard/UsageChartViewModel.kt index 4ac74c8..c7bdc95 100644 --- a/app/src/main/java/com/rainy/token/ui/dashboard/UsageChartViewModel.kt +++ b/app/src/main/java/com/rainy/token/ui/dashboard/UsageChartViewModel.kt @@ -153,10 +153,14 @@ class UsageChartViewModel @Inject constructor( // 降级后或用户手动选择 → 关闭降级,正常显示 allowFallback = false + // 图例数据源:只保留当前显示范围内实际出现过的模型 + val rangeModels = result.flatMap { it.byModel.keys }.distinct().sorted() + _state.update { it.copy( buckets = result, allModels = allModels, + rangeModels = rangeModels, loading = false ) } @@ -262,6 +266,8 @@ data class ChartUiState( val loading: Boolean = true, val granularity: ChartGranularity = ChartGranularity.LAST_5H_HOURLY, val allModels: List = emptyList(), + /** 当前显示范围内实际出现过的模型(图例只显示这些) */ + val rangeModels: List = emptyList(), val selectedModels: Set = emptySet(), // empty = all val buckets: List = emptyList(), val useUtc8: Boolean = false, diff --git a/app/src/main/java/com/rainy/token/ui/dashboard/UsageDetailScreen.kt b/app/src/main/java/com/rainy/token/ui/dashboard/UsageDetailScreen.kt index df2a2cd..2a058d2 100644 --- a/app/src/main/java/com/rainy/token/ui/dashboard/UsageDetailScreen.kt +++ b/app/src/main/java/com/rainy/token/ui/dashboard/UsageDetailScreen.kt @@ -53,6 +53,7 @@ import androidx.compose.ui.graphics.Color import androidx.compose.ui.platform.LocalContext import androidx.compose.ui.res.stringResource 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.sp import androidx.hilt.navigation.compose.hiltViewModel @@ -110,7 +111,23 @@ fun UsageDetailScreen( snackbarHost = { SnackbarHost(snackbarHostState) }, topBar = { 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)) } }, actions = { if (clearViewModel != null) { @@ -209,7 +226,7 @@ fun UsageDetailScreen( if (wideEnough) { // 面板够宽:前两张图表并排 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 reqTotal = state.buckets.sumOf { it.totalRequests } Row( @@ -256,7 +273,7 @@ fun UsageDetailScreen( } else { // 窄面板:三张图表纵向堆叠 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 } ChartCard(stringResource(R.string.chart_cost), "$${String.format(Locale.US, "%.4f", costTotal)}", { showCostDetail = true }) { StackedBarChart(state.buckets, @@ -331,7 +348,7 @@ fun UsageDetailScreen( 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 }) { 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)}") } }