feat(chart): 用量详情页显示服务标识;图例只显示当前时间范围内模型(含 tooltip 残留修复)
This commit is contained in:
parent
eecf54986f
commit
a079a0bfcb
@ -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
|
||||||
|
|||||||
@ -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,
|
||||||
|
|||||||
@ -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)}") }
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user