refactor: 积分榜导入导出改为右上角三点菜单,支持文件导出导入

This commit is contained in:
flykhan 2026-04-26 17:54:09 +08:00
parent da9828f4b2
commit 093a759ad4

View File

@ -3,7 +3,10 @@ package com.unogame.ui.screens
import android.content.ClipData import android.content.ClipData
import android.content.ClipboardManager import android.content.ClipboardManager
import android.content.Context import android.content.Context
import android.net.Uri
import android.widget.Toast import android.widget.Toast
import androidx.activity.compose.rememberLauncherForActivityResult
import androidx.activity.result.contract.ActivityResultContracts
import androidx.compose.foundation.background import androidx.compose.foundation.background
import androidx.compose.foundation.clickable import androidx.compose.foundation.clickable
import androidx.compose.foundation.horizontalScroll import androidx.compose.foundation.horizontalScroll
@ -43,8 +46,49 @@ fun ScoreboardScreen(onBack: () -> Unit) {
var detailEntry by remember { mutableStateOf<ScoreEntry?>(null) } var detailEntry by remember { mutableStateOf<ScoreEntry?>(null) }
var showImportDialog by remember { mutableStateOf(false) } var showImportDialog by remember { mutableStateOf(false) }
var importJson by remember { mutableStateOf("") } var importJson by remember { mutableStateOf("") }
var showMenu by remember { mutableStateOf(false) }
val gson = remember { Gson() } val gson = remember { Gson() }
// Export file launcher
val exportLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.CreateDocument("application/json")
) { uri: Uri? ->
uri?.let {
try {
val json = gson.toJson(scores)
context.contentResolver.openOutputStream(it)?.use { out ->
out.write(json.toByteArray())
}
Toast.makeText(context, "备份已导出", Toast.LENGTH_SHORT).show()
} catch (e: Exception) {
Toast.makeText(context, "导出失败: ${e.message}", Toast.LENGTH_LONG).show()
}
}
}
// Import file launcher
val importLauncher = rememberLauncherForActivityResult(
ActivityResultContracts.OpenDocument()
) { uri: Uri? ->
uri?.let {
try {
val json = context.contentResolver.openInputStream(it)?.bufferedReader()?.readText() ?: ""
val imported = gson.fromJson(json, Array<ScoreEntry>::class.java)?.toList() ?: emptyList()
if (imported.isNotEmpty()) {
val merged = (scores + imported).distinctBy { e -> "${e.name}_${e.mode}_${e.date}" }
.sortedByDescending { e -> e.points }.take(50)
Scoreboard.saveScores(context, merged)
scores = merged
Toast.makeText(context, "成功导入 ${imported.size} 条记录", Toast.LENGTH_SHORT).show()
} else {
Toast.makeText(context, "文件中无有效数据", Toast.LENGTH_SHORT).show()
}
} catch (e: Exception) {
Toast.makeText(context, "导入失败: ${e.message}", Toast.LENGTH_LONG).show()
}
}
}
val filtered = if (selectedFilter == "全部") scores val filtered = if (selectedFilter == "全部") scores
else scores.filter { it.mode == selectedFilter } else scores.filter { it.mode == selectedFilter }
@ -57,42 +101,55 @@ fun ScoreboardScreen(onBack: () -> Unit) {
IconButton(onClick = onBack) { IconButton(onClick = onBack) {
Icon(Icons.Default.ArrowBack, "返回", tint = Color.White) Icon(Icons.Default.ArrowBack, "返回", tint = Color.White)
} }
Text("积分排行榜", fontSize = 24.sp, fontWeight = FontWeight.Bold, color = Color.White) Text("积分排行榜", fontSize = 24.sp, fontWeight = FontWeight.Bold, color = Color.White, modifier = Modifier.weight(1f))
Box {
IconButton(onClick = { showMenu = true }) {
Icon(Icons.Default.MoreVert, "菜单", tint = Color.White)
} }
DropdownMenu(
Spacer(modifier = Modifier.height(16.dp)) expanded = showMenu,
onDismissRequest = { showMenu = false }
// Import / Export buttons
Row(
modifier = Modifier.fillMaxWidth(),
horizontalArrangement = Arrangement.spacedBy(8.dp)
) { ) {
OutlinedButton( DropdownMenuItem(
text = { Text("导出到文件") },
onClick = { onClick = {
showMenu = false
exportLauncher.launch("uno_scoreboard_backup.json")
},
leadingIcon = { Icon(Icons.Default.FileUpload, null) }
)
DropdownMenuItem(
text = { Text("从文件导入") },
onClick = {
showMenu = false
importLauncher.launch(arrayOf("application/json", "*/*"))
},
leadingIcon = { Icon(Icons.Default.FileDownload, null) }
)
DropdownMenuItem(
text = { Text("复制备份") },
onClick = {
showMenu = false
val json = gson.toJson(scores) val json = gson.toJson(scores)
val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager val clipboard = context.getSystemService(Context.CLIPBOARD_SERVICE) as ClipboardManager
clipboard.setPrimaryClip(ClipData.newPlainText("uno_scoreboard", json)) clipboard.setPrimaryClip(ClipData.newPlainText("uno_scoreboard", json))
Toast.makeText(context, "已复制备份JSON到剪贴板", Toast.LENGTH_SHORT).show() Toast.makeText(context, "已复制JSON到剪贴板", Toast.LENGTH_SHORT).show()
}, },
modifier = Modifier.weight(1f), leadingIcon = { Icon(Icons.Default.ContentCopy, null) }
colors = ButtonDefaults.outlinedButtonColors(contentColor = Color.White.copy(alpha = 0.7f)) )
) { DropdownMenuItem(
Icon(Icons.Default.FileUpload, null, modifier = Modifier.size(16.dp)) text = { Text("粘贴导入") },
Spacer(modifier = Modifier.width(4.dp)) onClick = {
Text("导出备份", fontSize = 12.sp) showMenu = false
showImportDialog = true
},
leadingIcon = { Icon(Icons.Default.ContentPaste, null) }
)
} }
OutlinedButton(
onClick = { showImportDialog = true },
modifier = Modifier.weight(1f),
colors = ButtonDefaults.outlinedButtonColors(contentColor = Color.White.copy(alpha = 0.7f))
) {
Icon(Icons.Default.FileDownload, null, modifier = Modifier.size(16.dp))
Spacer(modifier = Modifier.width(4.dp))
Text("导入备份", fontSize = 12.sp)
} }
} }
Spacer(modifier = Modifier.height(12.dp)) Spacer(modifier = Modifier.height(16.dp))
// Mode filter chips // Mode filter chips
if (scores.isNotEmpty()) { if (scores.isNotEmpty()) {
@ -282,7 +339,7 @@ fun ScoreboardScreen(onBack: () -> Unit) {
if (showImportDialog) { if (showImportDialog) {
AlertDialog( AlertDialog(
onDismissRequest = { showImportDialog = false }, onDismissRequest = { showImportDialog = false },
title = { Text("导入备份", color = GoldAccent) }, title = { Text("粘贴导入", color = GoldAccent) },
text = { text = {
Column { Column {
Text("粘贴之前导出的JSON数据", color = Color.White.copy(alpha = 0.7f), fontSize = 14.sp) Text("粘贴之前导出的JSON数据", color = Color.White.copy(alpha = 0.7f), fontSize = 14.sp)