feat: 皮肤站玩家管理补充二次确认、切换正版入口与默认徽章
Sync source to CNB / Sync Git ref (push) Skipped

- 删除/重命名/切换正版改为两段式确认,取消可整体复位
- 玩家列表与详情区均提供切换正版按钮
- 默认角色在列表和详情区显示「默认」徽章
- 补 rename/delete/mojang 三组确认文案
This commit is contained in:
AxTps committed 2026-10-21 22:25:30 +08:00
1 parent bc37e5d436
commit 9c92e157a5
3 files changed
+168 -2

No files matched your search

@@ -88,6 +88,18 @@ const messages = defineMessages({
id: 'app.skin-site.profile-view.mojang-toggled',
defaultMessage: 'Mojang status updated.',
},
deleteConfirm: {
id: 'app.skin-site.profile-view.delete-confirm',
defaultMessage: 'Delete this player? This cannot be undone.',
},
mojangConfirm: {
id: 'app.skin-site.profile-view.mojang-confirm',
defaultMessage: 'Toggle Mojang status for this player?',
},
renameConfirm: {
id: 'app.skin-site.profile-view.rename-confirm',
defaultMessage: 'Rename this player?',
},
})
const selectedId = ref<string | null>(null)
const texture = ref<SkinSitePlayerSkin>({})
@@ -151,14 +163,18 @@ const showAdd = ref(false)
const newName = ref('')
const renamingId = ref<string | null>(null)
const renameValue = ref('')
const confirmingRename = ref(false)
const confirmingDeleteId = ref<string | null>(null)
const confirmingMojangId = ref<string | null>(null)
function resetDialogs() {
showAdd.value = false
newName.value = ''
renamingId.value = null
renameValue.value = ''
confirmingRename.value = false
confirmingDeleteId.value = null
confirmingMojangId.value = null
}
async function submitAdd() {
@@ -182,11 +198,22 @@ async function submitAdd() {
function startRename(player: { uuid: string; name: string }) {
renamingId.value = player.uuid
renameValue.value = player.name
confirmingRename.value = false
}
function requestRename() {
const name = renameValue.value.trim()
if (!renamingId.value || !name) return
confirmingRename.value = true
}
async function submitRename() {
const name = renameValue.value.trim()
if (!renamingId.value || !name || busy.value) return
if (!confirmingRename.value) {
confirmingRename.value = true
return
}
busy.value = 'rename'
try {
const res = await skinSiteRenamePlayer(renamingId.value, name)
@@ -201,8 +228,17 @@ async function submitRename() {
}
}
function requestDelete(uuid: string) {
if (busy.value) return
confirmingDeleteId.value = uuid
}
async function submitDelete(uuid: string) {
if (busy.value) return
if (confirmingDeleteId.value !== uuid) {
confirmingDeleteId.value = uuid
return
}
busy.value = 'delete'
try {
const res = await skinSiteDeletePlayer(uuid)
@@ -249,8 +285,17 @@ async function clearDefaultPlayer() {
}
}
function requestToggleMojang(uuid: string) {
if (busy.value) return
confirmingMojangId.value = uuid
}
async function toggleMojang(uuid: string) {
if (busy.value) return
if (confirmingMojangId.value !== uuid) {
confirmingMojangId.value = uuid
return
}
busy.value = 'mojang'
try {
const res = await skinSiteSwitchMojang(uuid)
@@ -377,6 +422,39 @@ watch(signedIn, (value) => {
</div>
</div>
<div
v-if="renamingId"
class="flex flex-col gap-1.5 rounded-xl bg-button-bg p-2"
>
<input
v-model="renameValue"
type="text"
:placeholder="formatMessage(messages.playerNamePlaceholder)"
class="w-full rounded-lg border border-solid border-surface-5 bg-bg px-2 py-1 text-xs text-contrast outline-none focus:border-brand"
@keyup.enter="submitRename"
/>
<p v-if="confirmingRename" class="m-0 text-[11px] text-secondary">
{{ formatMessage(messages.renameConfirm) }}
</p>
<div class="flex gap-1.5">
<button
type="button"
class="flex-1 rounded-lg bg-brand px-2 py-1 text-xs font-semibold text-contrast disabled:opacity-50"
:disabled="busy === 'rename' || !renameValue.trim()"
@click="submitRename"
>
{{ formatMessage(confirmingRename ? messages.confirm : messages.rename) }}
</button>
<button
type="button"
class="rounded-lg bg-bg-raised px-2 py-1 text-xs font-semibold text-secondary"
@click="resetDialogs"
>
{{ formatMessage(messages.cancel) }}
</button>
</div>
</div>
<div v-if="playersLoading" class="flex items-center gap-2 text-sm text-secondary">
<SpinnerIcon class="size-4 animate-spin" />
{{ formatMessage(messages.loading) }}
@@ -403,6 +481,12 @@ watch(signedIn, (value) => {
class="!rounded-md"
/>
<span class="min-w-0 flex-1 truncate text-sm font-medium">{{ player.name }}</span>
<span
v-if="defaultUuid === player.uuid"
class="shrink-0 rounded bg-brand px-1.5 py-0.5 text-[10px] font-semibold text-contrast"
>
{{ formatMessage(messages.isDefault) }}
</span>
<span v-if="player.isMojang" class="shrink-0 text-[10px] uppercase text-secondary"
>Mojang</span
>
@@ -411,6 +495,7 @@ watch(signedIn, (value) => {
v-if="defaultUuid === player.uuid"
type="button"
class="rounded px-1.5 py-0.5 text-[10px] font-semibold text-brand"
:disabled="busy !== null"
@click.stop="clearDefaultPlayer"
>
{{ formatMessage(messages.clearDefault) }}
@@ -419,6 +504,7 @@ watch(signedIn, (value) => {
v-else
type="button"
class="rounded px-1.5 py-0.5 text-[10px] font-semibold text-secondary hover:bg-bg"
:disabled="busy !== null"
@click.stop="setDefaultPlayer(player.uuid)"
>
{{ formatMessage(messages.setDefault) }}
@@ -426,20 +512,67 @@ watch(signedIn, (value) => {
<button
type="button"
class="rounded px-1.5 py-0.5 text-[10px] font-semibold text-secondary hover:bg-bg"
:disabled="busy !== null"
@click.stop="startRename(player)"
>
{{ formatMessage(messages.rename) }}
</button>
<button
type="button"
class="rounded px-1.5 py-0.5 text-[10px] font-semibold text-red hover:bg-bg"
class="rounded px-1.5 py-0.5 text-[10px] font-semibold text-secondary hover:bg-bg"
:disabled="busy !== null"
@click.stop="requestToggleMojang(player.uuid)"
>
{{ formatMessage(messages.toggleMojang) }}
</button>
<button
v-if="confirmingDeleteId === player.uuid"
type="button"
class="rounded bg-red px-1.5 py-0.5 text-[10px] font-semibold text-contrast"
:disabled="busy !== null"
@click.stop="submitDelete(player.uuid)"
>
{{ formatMessage(messages.confirm) }}
</button>
<button
v-else
type="button"
class="rounded px-1.5 py-0.5 text-[10px] font-semibold text-red hover:bg-bg"
:disabled="busy !== null"
@click.stop="requestDelete(player.uuid)"
>
{{ formatMessage(messages.delete) }}
</button>
<button
v-if="confirmingDeleteId === player.uuid || confirmingMojangId === player.uuid"
type="button"
class="rounded px-1.5 py-0.5 text-[10px] font-semibold text-secondary hover:bg-bg"
@click.stop="resetDialogs"
>
{{ formatMessage(messages.cancel) }}
</button>
</div>
</div>
<div
v-if="confirmingDeleteId === player.uuid"
class="rounded-lg bg-button-bg px-2 py-1 text-[11px] text-red"
>
{{ formatMessage(messages.deleteConfirm) }}
</div>
<div
v-if="confirmingMojangId === player.uuid"
class="rounded-lg bg-button-bg px-2 py-1 text-[11px] text-secondary"
>
{{ formatMessage(messages.mojangConfirm) }}
<button
type="button"
class="ml-1 rounded bg-brand px-1.5 py-0.5 text-[10px] font-semibold text-contrast"
:disabled="busy !== null"
@click.stop="toggleMojang(player.uuid)"
>
{{ formatMessage(messages.confirm) }}
</button>
</div>
</div>
<!-- Preview + texture actions -->
@@ -451,10 +584,37 @@ watch(signedIn, (value) => {
</p>
<template v-else>
<div class="flex items-center gap-2">
<div class="flex flex-wrap items-center gap-2">
<h3 class="m-0 text-base font-semibold text-contrast">{{ selectedPlayer.name }}</h3>
<span
v-if="defaultUuid === selectedPlayer.uuid"
class="rounded bg-brand px-1.5 py-0.5 text-[10px] font-semibold text-contrast"
>
{{ formatMessage(messages.isDefault) }}
</span>
<span v-if="selectedPlayer.isMojang" class="text-xs text-secondary">Mojang</span>
<button
v-if="confirmingMojangId === selectedPlayer.uuid"
type="button"
class="rounded-full bg-brand px-2.5 py-1 text-xs font-semibold text-contrast"
:disabled="busy !== null"
@click="toggleMojang(selectedPlayer.uuid)"
>
{{ formatMessage(messages.confirm) }}
</button>
<button
v-else
type="button"
class="rounded-full bg-button-bg px-2.5 py-1 text-xs font-semibold text-secondary hover:brightness-110"
:disabled="busy !== null"
@click="requestToggleMojang(selectedPlayer.uuid)"
>
{{ formatMessage(messages.toggleMojang) }}
</button>
</div>
<p v-if="confirmingMojangId === selectedPlayer.uuid" class="m-0 text-xs text-secondary">
{{ formatMessage(messages.mojangConfirm) }}
</p>
<div class="relative h-80 w-full overflow-hidden rounded-xl bg-button-bg">
<div v-if="loadingTexture" class="absolute inset-0 flex items-center justify-center">
@@ -74,10 +74,12 @@
"app.skin-site.profile-view.confirm": { "message": "Confirm" },
"app.skin-site.profile-view.default-set": { "message": "Default player updated." },
"app.skin-site.profile-view.delete": { "message": "Delete" },
"app.skin-site.profile-view.delete-confirm": { "message": "Delete this player? This cannot be undone." },
"app.skin-site.profile-view.deleted": { "message": "Player deleted." },
"app.skin-site.profile-view.elytra": { "message": "Elytra" },
"app.skin-site.profile-view.is-default": { "message": "Default" },
"app.skin-site.profile-view.loading": { "message": "Loading players…" },
"app.skin-site.profile-view.mojang-confirm": { "message": "Toggle Mojang status for this player?" },
"app.skin-site.profile-view.mojang-toggled": { "message": "Mojang status updated." },
"app.skin-site.profile-view.no-players": { "message": "No players yet." },
"app.skin-site.profile-view.player-name-placeholder": { "message": "Player name" },
@@ -87,6 +89,7 @@
"app.skin-site.profile-view.remove": { "message": "Remove" },
"app.skin-site.profile-view.removed": { "message": "Texture removed." },
"app.skin-site.profile-view.rename": { "message": "Rename" },
"app.skin-site.profile-view.rename-confirm": { "message": "Rename this player?" },
"app.skin-site.profile-view.renamed": { "message": "Player renamed." },
"app.skin-site.profile-view.select-hint": {
"message": "Select a player to preview and edit its textures."
@@ -70,10 +70,12 @@
"app.skin-site.profile-view.confirm": { "message": "确认" },
"app.skin-site.profile-view.default-set": { "message": "默认角色已更新。" },
"app.skin-site.profile-view.delete": { "message": "删除" },
"app.skin-site.profile-view.delete-confirm": { "message": "删除此角色?此操作不可撤销。" },
"app.skin-site.profile-view.deleted": { "message": "角色已删除。" },
"app.skin-site.profile-view.elytra": { "message": "鞘翅" },
"app.skin-site.profile-view.is-default": { "message": "默认" },
"app.skin-site.profile-view.loading": { "message": "正在加载角色…" },
"app.skin-site.profile-view.mojang-confirm": { "message": "切换此角色的正版状态?" },
"app.skin-site.profile-view.mojang-toggled": { "message": "正版状态已更新。" },
"app.skin-site.profile-view.no-players": { "message": "暂无角色。" },
"app.skin-site.profile-view.player-name-placeholder": { "message": "角色名" },
@@ -83,6 +85,7 @@
"app.skin-site.profile-view.remove": { "message": "移除" },
"app.skin-site.profile-view.removed": { "message": "材质已移除。" },
"app.skin-site.profile-view.rename": { "message": "重命名" },
"app.skin-site.profile-view.rename-confirm": { "message": "重命名此角色?" },
"app.skin-site.profile-view.renamed": { "message": "角色已重命名。" },
"app.skin-site.profile-view.select-hint": {
"message": "选择角色以预览和修改其材质。"