fix(sub2api): 面板管理只保留首页入口,弹窗支持滚动、操作前置
按使用反馈调整(真机验证): - 详情页不再重复展示面板管理卡:Key 列表/新建/分组/兑换统一收到首页 Sub2API 卡片的 「面板管理」入口,详情页只保留用量明细(周期切换 + 柱状图 + 按模型)。 随之移除详情页已不再使用的 panelKeysState / panelGroups / panelActionMessage 状态收集。 - 修复「面板管理弹窗打开后不能滚动」:AlertDialog 的 text 槽是固定高度且不自带滚动容器, 内容超过上限会被直接裁剪。改为给 text 槽加 verticalScroll + 高度上限(屏幕高度 72%), 现在可以正常上下滚动浏览全部 Key。 - 主要操作前置:新建 Key / 兑换卡密 从列表底部移到顶部(原位置在 20+ 条 Key 之后, 需要滚到底才能看到);并新增「API Key 列表」小标题区分列表区。 - 弹窗内不再重复标题:组件新增 showHeader 参数,首页弹窗由 AlertDialog title 槽显示标题, 组件内部标题隐藏(刷新按钮保留)。 验证:compileDebugKotlin、lintDebug 通过;真机(V2172A / Android 15)实测 首页仍只显示今日与近7天、弹窗可滚动、新建/兑换在首屏、详情页无面板管理卡。 注:gradle.properties 的本地路径开关仍未纳入提交。
This commit is contained in:
parent
58d8b0e077
commit
eebe9f6987
@ -19,12 +19,14 @@ import androidx.compose.foundation.layout.Spacer
|
||||
import androidx.compose.foundation.layout.fillMaxSize
|
||||
import androidx.compose.foundation.layout.fillMaxWidth
|
||||
import androidx.compose.foundation.layout.height
|
||||
import androidx.compose.foundation.layout.heightIn
|
||||
import androidx.compose.foundation.layout.offset
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.layout.size
|
||||
import androidx.compose.foundation.layout.width
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.ui.platform.LocalConfiguration
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.icons.Icons
|
||||
@ -373,26 +375,39 @@ fun DashboardScreen(
|
||||
}
|
||||
}
|
||||
|
||||
// Sub2API 面板管理弹窗:与详情页共用同一个面板管理组件
|
||||
// Sub2API 面板管理弹窗
|
||||
// 滚动说明:AlertDialog 的 text 槽是固定高度且**不自带滚动容器**,内容超过上限会被直接裁掉
|
||||
// (表现为「打开后滚不动、下面的 Key 看不到」)。这里给 text 槽加 verticalScroll + 高度上限,
|
||||
// 让整个面板管理内容(Key 列表/新建/分组/兑换)可以正常上下滚动。
|
||||
if (showSub2PanelManagement) {
|
||||
val scrollState = rememberScrollState()
|
||||
val maxDialogHeight = (LocalConfiguration.current.screenHeightDp * 0.72f).dp
|
||||
AlertDialog(
|
||||
onDismissRequest = { showSub2PanelManagement = false },
|
||||
title = { Text(stringResource(R.string.sub2_panel_manage_title), fontWeight = FontWeight.Bold) },
|
||||
text = {
|
||||
com.rainy.token.ui.servicedetail.Sub2ApiPanelManagement(
|
||||
balance = uiState.cards.firstOrNull { it.service == ServiceType.SUB2API }?.cachedBalance?.balance,
|
||||
keysState = panelKeysState,
|
||||
groups = panelGroups,
|
||||
actionMessage = panelActionMessage,
|
||||
createdKey = recentlyCreatedKey,
|
||||
onLoadKeys = { viewModel.loadPanelKeys() },
|
||||
onLoadGroups = { viewModel.loadPanelGroups() },
|
||||
onCreateKey = viewModel::createPanelKey,
|
||||
onSwitchGroup = viewModel::switchPanelGroup,
|
||||
onRedeem = viewModel::redeemPanelCode,
|
||||
onConsumeMessage = viewModel::consumePanelActionMessage,
|
||||
onConsumeCreatedKey = viewModel::consumeRecentlyCreatedKey
|
||||
)
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(max = maxDialogHeight)
|
||||
.verticalScroll(scrollState)
|
||||
) {
|
||||
com.rainy.token.ui.servicedetail.Sub2ApiPanelManagement(
|
||||
balance = uiState.cards.firstOrNull { it.service == ServiceType.SUB2API }?.cachedBalance?.balance,
|
||||
keysState = panelKeysState,
|
||||
groups = panelGroups,
|
||||
actionMessage = panelActionMessage,
|
||||
createdKey = recentlyCreatedKey,
|
||||
onLoadKeys = { viewModel.loadPanelKeys() },
|
||||
onLoadGroups = { viewModel.loadPanelGroups() },
|
||||
onCreateKey = viewModel::createPanelKey,
|
||||
onSwitchGroup = viewModel::switchPanelGroup,
|
||||
onRedeem = viewModel::redeemPanelCode,
|
||||
onConsumeMessage = viewModel::consumePanelActionMessage,
|
||||
onConsumeCreatedKey = viewModel::consumeRecentlyCreatedKey,
|
||||
showHeader = false
|
||||
)
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
TextButton(onClick = { showSub2PanelManagement = false }) {
|
||||
|
||||
@ -117,10 +117,8 @@ fun ServiceDetailScreen(
|
||||
val selectedModel by viewModel.selectedModel.collectAsStateWithLifecycle()
|
||||
val modelsLoading by viewModel.modelsLoading.collectAsStateWithLifecycle()
|
||||
val checkinState by viewModel.checkinState.collectAsStateWithLifecycle()
|
||||
val panelKeysState by viewModel.panelKeysState.collectAsStateWithLifecycle()
|
||||
val panelActionMessage by viewModel.panelActionMessage.collectAsStateWithLifecycle()
|
||||
val panelGroups by viewModel.panelGroups.collectAsStateWithLifecycle()
|
||||
val recentlyCreatedKey by viewModel.recentlyCreatedKey.collectAsStateWithLifecycle()
|
||||
// 面板管理(Key/分组/兑换)已移到首页 Sub2API 卡片的「面板管理」入口,
|
||||
// 详情页只保留用量明细所需的窗口统计状态。
|
||||
val panelWindowStats by viewModel.panelWindowStats.collectAsStateWithLifecycle()
|
||||
val config = ServiceConfigProvider.get(service)
|
||||
val isManualMode = config.method == FetchMethod.MANUAL
|
||||
@ -210,24 +208,8 @@ fun ServiceDetailScreen(
|
||||
onLoadWindowModelStats = { from, to -> viewModel.loadPanelWindowModelStats(from, to) }
|
||||
)
|
||||
}
|
||||
// 面板管理:Key 列表/新建/分组/兑换/低余额提醒(有面板 Token 才加载)
|
||||
item {
|
||||
Sub2ApiPanelManagement(
|
||||
balance = (uiState.state as? State.Fresh)?.data
|
||||
?: (uiState.state as? State.Stale)?.data,
|
||||
keysState = panelKeysState,
|
||||
groups = panelGroups,
|
||||
actionMessage = panelActionMessage,
|
||||
createdKey = recentlyCreatedKey,
|
||||
onLoadKeys = { viewModel.loadPanelKeys() },
|
||||
onLoadGroups = { viewModel.loadPanelGroups() },
|
||||
onCreateKey = viewModel::createPanelKey,
|
||||
onSwitchGroup = viewModel::switchPanelGroup,
|
||||
onRedeem = viewModel::redeemPanelCode,
|
||||
onConsumeMessage = viewModel::consumePanelActionMessage,
|
||||
onConsumeCreatedKey = viewModel::consumeRecentlyCreatedKey
|
||||
)
|
||||
}
|
||||
// 面板管理(Key 列表/新建/分组/兑换)不再在详情页展示:
|
||||
// 入口统一放在首页 Sub2API 卡片的「面板管理」按钮,避免同页重复且详情页过长。
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -75,7 +75,9 @@ internal fun Sub2ApiPanelManagement(
|
||||
onSwitchGroup: (Long, Long) -> Unit,
|
||||
onRedeem: (String) -> Unit,
|
||||
onConsumeMessage: () -> Unit,
|
||||
onConsumeCreatedKey: () -> Unit = {}
|
||||
onConsumeCreatedKey: () -> Unit = {},
|
||||
/** 弹窗场景由 AlertDialog 的 title 槽展示标题,可关闭内部标题避免重复 */
|
||||
showHeader: Boolean = true
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
var showCreate by remember { mutableStateOf(false) }
|
||||
@ -110,15 +112,25 @@ internal fun Sub2ApiPanelManagement(
|
||||
colors = CardDefaults.cardColors(containerColor = MaterialTheme.colorScheme.surfaceVariant.copy(alpha = 0.4f))
|
||||
) {
|
||||
Column(modifier = Modifier.fillMaxWidth().padding(16.dp)) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = stringResource(R.string.sub2_panel_manage_title),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
IconButton(onClick = { onLoadKeys(); onLoadGroups() }) {
|
||||
Icon(Icons.Filled.Refresh, contentDescription = stringResource(R.string.action_refresh))
|
||||
if (showHeader) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
text = stringResource(R.string.sub2_panel_manage_title),
|
||||
style = MaterialTheme.typography.titleMedium,
|
||||
fontWeight = FontWeight.Bold
|
||||
)
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
IconButton(onClick = { onLoadKeys(); onLoadGroups() }) {
|
||||
Icon(Icons.Filled.Refresh, contentDescription = stringResource(R.string.action_refresh))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// 标题由外部弹窗提供时,这里保留一个刷新按钮,方便手动重新拉取 Key/分组
|
||||
Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth()) {
|
||||
Spacer(modifier = Modifier.weight(1f))
|
||||
IconButton(onClick = { onLoadKeys(); onLoadGroups() }) {
|
||||
Icon(Icons.Filled.Refresh, contentDescription = stringResource(R.string.action_refresh))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ -154,6 +166,26 @@ internal fun Sub2ApiPanelManagement(
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
// 主要操作(新建 Key / 兑换卡密)固定在列表上方:
|
||||
// Key 数量多时列表很长,放在底部会被埋在滚动区尽头,不易发现。
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
Button(onClick = { onLoadGroups(); showCreate = true }, modifier = Modifier.weight(1f)) {
|
||||
Text(stringResource(R.string.sub2_panel_create))
|
||||
}
|
||||
OutlinedButton(onClick = { showRedeem = true }, modifier = Modifier.weight(1f)) {
|
||||
Text(stringResource(R.string.sub2_panel_redeem))
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(12.dp))
|
||||
|
||||
Text(
|
||||
text = stringResource(R.string.sub2_panel_keys_header),
|
||||
style = MaterialTheme.typography.labelLarge,
|
||||
color = inkMuted()
|
||||
)
|
||||
Spacer(modifier = Modifier.height(4.dp))
|
||||
|
||||
when (keysState) {
|
||||
is PanelKeysState.Idle -> {
|
||||
OutlinedButton(onClick = onLoadKeys, modifier = Modifier.fillMaxWidth()) {
|
||||
@ -225,16 +257,6 @@ internal fun Sub2ApiPanelManagement(
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(modifier = Modifier.height(10.dp))
|
||||
|
||||
Row(horizontalArrangement = Arrangement.spacedBy(8.dp)) {
|
||||
OutlinedButton(onClick = { onLoadGroups(); showCreate = true }, modifier = Modifier.weight(1f)) {
|
||||
Text(stringResource(R.string.sub2_panel_create))
|
||||
}
|
||||
OutlinedButton(onClick = { showRedeem = true }, modifier = Modifier.weight(1f)) {
|
||||
Text(stringResource(R.string.sub2_panel_redeem))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -672,4 +672,5 @@
|
||||
<string name="sub2_panel_copied">API Key 已复制</string>
|
||||
<string name="sub2_panel_low_balance">⚠ 余额不足:剩余 $%1$s,请及时充值或兑换卡密</string>
|
||||
<string name="sub2_models_alltime_suffix">(累计口径,未按所选周期过滤)</string>
|
||||
<string name="sub2_panel_keys_header">API Key 列表</string>
|
||||
</resources>
|
||||
@ -672,4 +672,5 @@
|
||||
<string name="sub2_panel_copied">API Key 已複製</string>
|
||||
<string name="sub2_panel_low_balance">⚠ 餘額不足:剩餘 $%1$s,請及時儲值或兌換卡密</string>
|
||||
<string name="sub2_models_alltime_suffix">(累計口徑,未按所選週期過濾)</string>
|
||||
<string name="sub2_panel_keys_header">API Key 列表</string>
|
||||
</resources>
|
||||
@ -677,4 +677,5 @@
|
||||
<string name="sub2_panel_copied">API Key copied</string>
|
||||
<string name="sub2_panel_low_balance">⚠ Low balance: $%1$s left — top up or redeem a code</string>
|
||||
<string name="sub2_models_alltime_suffix">(all-time, not limited to the selected period)</string>
|
||||
<string name="sub2_panel_keys_header">API keys</string>
|
||||
</resources>
|
||||
Loading…
Reference in New Issue
Block a user