1 parent
a4615c7b73
commit
77258eee9d
6 files changed
+265
-7
No files matched your search
@@ -30,15 +30,23 @@ const messages = defineMessages({
|
|||||||
error: { id: 'app.skin-site.home.error', defaultMessage: 'Could not load players.' },
|
error: { id: 'app.skin-site.home.error', defaultMessage: 'Could not load players.' },
|
||||||
signIn: { id: 'app.skin-site.home.sign-in', defaultMessage: 'Sign in' },
|
signIn: { id: 'app.skin-site.home.sign-in', defaultMessage: 'Sign in' },
|
||||||
settings: { id: 'app.skin-site.home.settings', defaultMessage: 'Account' },
|
settings: { id: 'app.skin-site.home.settings', defaultMessage: 'Account' },
|
||||||
|
admin: { id: 'app.skin-site.home.admin', defaultMessage: 'Admin' },
|
||||||
myPlayers: { id: 'app.skin-site.home.my-players', defaultMessage: 'My players' },
|
myPlayers: { id: 'app.skin-site.home.my-players', defaultMessage: 'My players' },
|
||||||
})
|
})
|
||||||
|
|
||||||
const emit = defineEmits<{ (e: 'sign-in' | 'open-settings' | 'open-players'): void }>()
|
const emit = defineEmits<{
|
||||||
|
(e: 'sign-in' | 'open-settings' | 'open-players' | 'open-admin'): void
|
||||||
|
}>()
|
||||||
|
|
||||||
const luck = ref<number | null>(null)
|
const luck = ref<number | null>(null)
|
||||||
const luckLoading = ref(false)
|
const luckLoading = ref(false)
|
||||||
|
|
||||||
const signedIn = computed(() => skinSiteStatus.value === 'signed-in' && !!skinSiteUser.value)
|
const signedIn = computed(() => skinSiteStatus.value === 'signed-in' && !!skinSiteUser.value)
|
||||||
|
|
||||||
|
const isAdmin = computed(() => {
|
||||||
|
const permissions = skinSiteUser.value?.permissions ?? []
|
||||||
|
return permissions.includes('WEB_ADMIN') || permissions.includes('SERVER_ADMIN')
|
||||||
|
})
|
||||||
const players = computed(() => skinSitePlayers.value)
|
const players = computed(() => skinSitePlayers.value)
|
||||||
const playersLoading = computed(
|
const playersLoading = computed(
|
||||||
() => skinSitePlayersStatus.value === 'checking' || skinSitePlayersStatus.value === 'idle',
|
() => skinSitePlayersStatus.value === 'checking' || skinSitePlayersStatus.value === 'idle',
|
||||||
@@ -98,6 +106,14 @@ watch(signedIn, loadLuck)
|
|||||||
>
|
>
|
||||||
{{ formatMessage(messages.settings) }}
|
{{ formatMessage(messages.settings) }}
|
||||||
</button>
|
</button>
|
||||||
|
<button
|
||||||
|
v-if="isAdmin"
|
||||||
|
type="button"
|
||||||
|
class="shrink-0 rounded-full bg-brand px-3 py-1 text-xs font-semibold text-contrast hover:brightness-110"
|
||||||
|
@click="emit('open-admin')"
|
||||||
|
>
|
||||||
|
{{ formatMessage(messages.admin) }}
|
||||||
|
</button>
|
||||||
<div
|
<div
|
||||||
class="ml-auto flex shrink-0 items-center gap-1 rounded-full bg-button-bg px-3 py-1 text-sm"
|
class="ml-auto flex shrink-0 items-center gap-1 rounded-full bg-button-bg px-3 py-1 text-sm"
|
||||||
:title="formatMessage(messages.luck)"
|
:title="formatMessage(messages.luck)"
|
||||||
|
|||||||
@@ -0,0 +1,161 @@
|
|||||||
|
<script setup lang="ts">
|
||||||
|
import { defineMessages, useVIntl } from '@modrinth/ui'
|
||||||
|
import { computed, ref } from 'vue'
|
||||||
|
|
||||||
|
import { skinSiteUser } from '@/composables/skin-site-session'
|
||||||
|
|
||||||
|
const emit = defineEmits<{ (e: 'back'): void }>()
|
||||||
|
|
||||||
|
const { formatMessage } = useVIntl()
|
||||||
|
|
||||||
|
const messages = defineMessages({
|
||||||
|
title: { id: 'app.skin-site.admin.title', defaultMessage: 'Admin console' },
|
||||||
|
back: { id: 'app.skin-site.admin.back', defaultMessage: 'Back to home' },
|
||||||
|
noAccess: { id: 'app.skin-site.admin.no-access', defaultMessage: 'You do not have access.' },
|
||||||
|
noAccessHint: {
|
||||||
|
id: 'app.skin-site.admin.no-access-hint',
|
||||||
|
defaultMessage: 'Sign in with an administrator account to use this console.',
|
||||||
|
},
|
||||||
|
groupSecurity: { id: 'app.skin-site.admin.group.security', defaultMessage: 'Security' },
|
||||||
|
groupUsers: { id: 'app.skin-site.admin.group.users', defaultMessage: 'Users' },
|
||||||
|
groupCommunity: { id: 'app.skin-site.admin.group.community', defaultMessage: 'Community' },
|
||||||
|
groupResources: { id: 'app.skin-site.admin.group.resources', defaultMessage: 'Resources' },
|
||||||
|
itemServers: { id: 'app.skin-site.admin.item.servers', defaultMessage: 'Servers' },
|
||||||
|
itemSummary: { id: 'app.skin-site.admin.item.summary', defaultMessage: 'Summary' },
|
||||||
|
itemAccessAudit: { id: 'app.skin-site.admin.item.access-audit', defaultMessage: 'Access audit' },
|
||||||
|
itemUsers: { id: 'app.skin-site.admin.item.users', defaultMessage: 'Users' },
|
||||||
|
itemPlayers: { id: 'app.skin-site.admin.item.players', defaultMessage: 'Players' },
|
||||||
|
itemReview: { id: 'app.skin-site.admin.item.review', defaultMessage: 'Player review' },
|
||||||
|
itemBroadcast: { id: 'app.skin-site.admin.item.broadcast', defaultMessage: 'Broadcast' },
|
||||||
|
itemChat: { id: 'app.skin-site.admin.item.chat', defaultMessage: 'Chat rooms' },
|
||||||
|
itemWiki: { id: 'app.skin-site.admin.item.wiki', defaultMessage: 'Wiki' },
|
||||||
|
itemDonations: { id: 'app.skin-site.admin.item.donations', defaultMessage: 'Donations' },
|
||||||
|
itemMods: { id: 'app.skin-site.admin.item.mods', defaultMessage: 'Mods' },
|
||||||
|
itemLauncher: { id: 'app.skin-site.admin.item.launcher', defaultMessage: 'Launcher' },
|
||||||
|
itemDownloads: { id: 'app.skin-site.admin.item.downloads', defaultMessage: 'Downloads' },
|
||||||
|
})
|
||||||
|
|
||||||
|
type AdminKey =
|
||||||
|
| 'servers'
|
||||||
|
| 'summary'
|
||||||
|
| 'access-audit'
|
||||||
|
| 'users'
|
||||||
|
| 'players'
|
||||||
|
| 'review'
|
||||||
|
| 'broadcast'
|
||||||
|
| 'chat'
|
||||||
|
| 'wiki'
|
||||||
|
| 'donations'
|
||||||
|
| 'mods'
|
||||||
|
| 'launcher'
|
||||||
|
| 'downloads'
|
||||||
|
|
||||||
|
interface NavItem {
|
||||||
|
key: AdminKey
|
||||||
|
label: string
|
||||||
|
}
|
||||||
|
interface NavGroup {
|
||||||
|
id: string
|
||||||
|
label: string
|
||||||
|
items: NavItem[]
|
||||||
|
}
|
||||||
|
|
||||||
|
const navGroups = computed<NavGroup[]>(() => [
|
||||||
|
{
|
||||||
|
id: 'security',
|
||||||
|
label: formatMessage(messages.groupSecurity),
|
||||||
|
items: [
|
||||||
|
{ key: 'servers', label: formatMessage(messages.itemServers) },
|
||||||
|
{ key: 'summary', label: formatMessage(messages.itemSummary) },
|
||||||
|
{ key: 'access-audit', label: formatMessage(messages.itemAccessAudit) },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'users',
|
||||||
|
label: formatMessage(messages.groupUsers),
|
||||||
|
items: [
|
||||||
|
{ key: 'users', label: formatMessage(messages.itemUsers) },
|
||||||
|
{ key: 'players', label: formatMessage(messages.itemPlayers) },
|
||||||
|
{ key: 'review', label: formatMessage(messages.itemReview) },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'community',
|
||||||
|
label: formatMessage(messages.groupCommunity),
|
||||||
|
items: [
|
||||||
|
{ key: 'broadcast', label: formatMessage(messages.itemBroadcast) },
|
||||||
|
{ key: 'chat', label: formatMessage(messages.itemChat) },
|
||||||
|
{ key: 'wiki', label: formatMessage(messages.itemWiki) },
|
||||||
|
{ key: 'donations', label: formatMessage(messages.itemDonations) },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
id: 'resources',
|
||||||
|
label: formatMessage(messages.groupResources),
|
||||||
|
items: [
|
||||||
|
{ key: 'mods', label: formatMessage(messages.itemMods) },
|
||||||
|
{ key: 'launcher', label: formatMessage(messages.itemLauncher) },
|
||||||
|
{ key: 'downloads', label: formatMessage(messages.itemDownloads) },
|
||||||
|
],
|
||||||
|
},
|
||||||
|
])
|
||||||
|
|
||||||
|
const hasAdminAccess = computed(() => {
|
||||||
|
const permissions = skinSiteUser.value?.permissions ?? []
|
||||||
|
return permissions.includes('WEB_ADMIN') || permissions.includes('SERVER_ADMIN')
|
||||||
|
})
|
||||||
|
|
||||||
|
const current = ref<AdminKey>('summary')
|
||||||
|
</script>
|
||||||
|
<template>
|
||||||
|
<div class="flex h-full min-h-0 flex-col gap-4">
|
||||||
|
<button
|
||||||
|
type="button"
|
||||||
|
class="flex w-fit items-center gap-1 rounded-full bg-button-bg px-3 py-1 text-xs font-semibold text-contrast hover:brightness-110"
|
||||||
|
@click="emit('back')"
|
||||||
|
>
|
||||||
|
← {{ formatMessage(messages.back) }}
|
||||||
|
</button>
|
||||||
|
|
||||||
|
<div
|
||||||
|
v-if="!hasAdminAccess"
|
||||||
|
class="flex flex-col gap-1 rounded-xl border border-solid border-surface-5 bg-bg-raised p-6"
|
||||||
|
>
|
||||||
|
<h2 class="m-0 text-lg font-bold text-contrast">{{ formatMessage(messages.noAccess) }}</h2>
|
||||||
|
<p class="m-0 text-sm text-secondary">{{ formatMessage(messages.noAccessHint) }}</p>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<div v-else class="flex min-h-0 flex-1 gap-4">
|
||||||
|
<nav class="flex w-48 shrink-0 flex-col gap-3 overflow-y-auto">
|
||||||
|
<div v-for="group in navGroups" :key="group.id" class="flex flex-col gap-1">
|
||||||
|
<span class="px-2 text-[10px] font-bold tracking-wide text-secondary uppercase">
|
||||||
|
{{ group.label }}
|
||||||
|
</span>
|
||||||
|
<button
|
||||||
|
v-for="item in group.items"
|
||||||
|
:key="item.key"
|
||||||
|
type="button"
|
||||||
|
class="rounded-lg px-2 py-1.5 text-left text-xs font-semibold transition-colors"
|
||||||
|
:class="
|
||||||
|
current === item.key
|
||||||
|
? 'bg-brand text-contrast'
|
||||||
|
: 'text-secondary hover:bg-button-bg hover:text-contrast'
|
||||||
|
"
|
||||||
|
@click="current = item.key"
|
||||||
|
>
|
||||||
|
{{ item.label }}
|
||||||
|
</button>
|
||||||
|
</div>
|
||||||
|
</nav>
|
||||||
|
|
||||||
|
<section
|
||||||
|
class="min-w-0 flex-1 overflow-y-auto rounded-xl border border-solid border-surface-5 bg-bg-raised p-4"
|
||||||
|
>
|
||||||
|
<h1 class="m-0 mb-3 text-xl font-bold text-contrast">
|
||||||
|
{{ formatMessage(messages.title) }} · {{ current }}
|
||||||
|
</h1>
|
||||||
|
<!-- Page content is filled in one admin screen at a time. -->
|
||||||
|
</section>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
@@ -4,6 +4,7 @@ import { defineMessages, injectNotificationManager, useVIntl } from '@modrinth/u
|
|||||||
import { ref } from 'vue'
|
import { ref } from 'vue'
|
||||||
|
|
||||||
import SkinSiteAccountCard from './SkinSiteAccountCard.vue'
|
import SkinSiteAccountCard from './SkinSiteAccountCard.vue'
|
||||||
|
import SkinSiteAdminView from './SkinSiteAdminView.vue'
|
||||||
import SkinSiteAnnouncements from './SkinSiteAnnouncements.vue'
|
import SkinSiteAnnouncements from './SkinSiteAnnouncements.vue'
|
||||||
import SkinSiteBugCreateModal from './SkinSiteBugCreateModal.vue'
|
import SkinSiteBugCreateModal from './SkinSiteBugCreateModal.vue'
|
||||||
import SkinSiteBugDetailView from './SkinSiteBugDetailView.vue'
|
import SkinSiteBugDetailView from './SkinSiteBugDetailView.vue'
|
||||||
@@ -35,7 +36,15 @@ const YGGDRASIL_URL = 'https://skin.starlight.cool/yggdrasil'
|
|||||||
const loginModal = ref<InstanceType<typeof SkinSiteLoginModal>>()
|
const loginModal = ref<InstanceType<typeof SkinSiteLoginModal>>()
|
||||||
const profileModal = ref<InstanceType<typeof SkinSiteProfileModal>>()
|
const profileModal = ref<InstanceType<typeof SkinSiteProfileModal>>()
|
||||||
const view = ref<
|
const view = ref<
|
||||||
'home' | 'players' | 'downloads' | 'map' | 'review' | 'bugs' | 'bug-detail' | 'bug-notifications'
|
| 'home'
|
||||||
|
| 'players'
|
||||||
|
| 'downloads'
|
||||||
|
| 'map'
|
||||||
|
| 'review'
|
||||||
|
| 'bugs'
|
||||||
|
| 'bug-detail'
|
||||||
|
| 'bug-notifications'
|
||||||
|
| 'admin'
|
||||||
>('home')
|
>('home')
|
||||||
const selectedBugId = ref<number | null>(null)
|
const selectedBugId = ref<number | null>(null)
|
||||||
|
|
||||||
@@ -88,6 +97,7 @@ async function copyAddress() {
|
|||||||
@back="view = 'bugs'"
|
@back="view = 'bugs'"
|
||||||
@open="openBug"
|
@open="openBug"
|
||||||
/>
|
/>
|
||||||
|
<SkinSiteAdminView v-else-if="view === 'admin'" @back="view = 'home'" />
|
||||||
<SkinSiteBugDetailView
|
<SkinSiteBugDetailView
|
||||||
v-else-if="view === 'bug-detail' && selectedBugId !== null"
|
v-else-if="view === 'bug-detail' && selectedBugId !== null"
|
||||||
:issue-id="selectedBugId"
|
:issue-id="selectedBugId"
|
||||||
@@ -104,6 +114,7 @@ async function copyAddress() {
|
|||||||
@sign-in="loginModal?.show()"
|
@sign-in="loginModal?.show()"
|
||||||
@open-settings="profileModal?.show()"
|
@open-settings="profileModal?.show()"
|
||||||
@open-players="view = 'players'"
|
@open-players="view = 'players'"
|
||||||
|
@open-admin="view = 'admin'"
|
||||||
/>
|
/>
|
||||||
|
|
||||||
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
<div class="grid grid-cols-1 gap-6 lg:grid-cols-2">
|
||||||
|
|||||||
@@ -19,7 +19,7 @@ import { fetchImageAsDataUrl, renderSkinHead } from '@/helpers/skin-site-render'
|
|||||||
|
|
||||||
export const SKIN_SITE_ORIGIN = 'https://skin.starlight.cool'
|
export const SKIN_SITE_ORIGIN = 'https://skin.starlight.cool'
|
||||||
|
|
||||||
export type SkinSiteUser = { uuid: string; username: string }
|
export type SkinSiteUser = { uuid: string; username: string; permissions: string[] }
|
||||||
export type SkinSiteStatus = 'checking' | 'signed-out' | 'signed-in' | 'error'
|
export type SkinSiteStatus = 'checking' | 'signed-out' | 'signed-in' | 'error'
|
||||||
export type SkinSitePlayer = {
|
export type SkinSitePlayer = {
|
||||||
uuid: string
|
uuid: string
|
||||||
@@ -46,7 +46,11 @@ export const skinSitePlayersStatus = readonly(playersStatus)
|
|||||||
export const selectedSkinSitePlayerId = readonly(selectedPlayerId)
|
export const selectedSkinSitePlayerId = readonly(selectedPlayerId)
|
||||||
|
|
||||||
function toUser(raw: ApiSkinSiteUser): SkinSiteUser {
|
function toUser(raw: ApiSkinSiteUser): SkinSiteUser {
|
||||||
return { uuid: raw.uuid, username: raw.username }
|
return {
|
||||||
|
uuid: raw.uuid,
|
||||||
|
username: raw.username,
|
||||||
|
permissions: Array.isArray(raw.permissions) ? raw.permissions : [],
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
function clearSessionState() {
|
function clearSessionState() {
|
||||||
@@ -111,7 +115,7 @@ export async function signInSkinSite(payload: {
|
|||||||
status.value = 'signed-in'
|
status.value = 'signed-in'
|
||||||
void requestSkinSitePlayers().catch(() => {})
|
void requestSkinSitePlayers().catch(() => {})
|
||||||
} else {
|
} else {
|
||||||
user.value = { uuid: '', username: result.payload.username }
|
user.value = { uuid: '', username: result.payload.username, permissions: [] }
|
||||||
status.value = 'signed-in'
|
status.value = 'signed-in'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -11767,5 +11767,38 @@
|
|||||||
"app.skin-site.home.subtitle": {"message": "The wardrobe for StarLight Server players."},
|
"app.skin-site.home.subtitle": {"message": "The wardrobe for StarLight Server players."},
|
||||||
"app.skin-site.home.address": {"message": "Skin site address"},
|
"app.skin-site.home.address": {"message": "Skin site address"},
|
||||||
"app.skin-site.home.copied": {"message": "Address copied."},
|
"app.skin-site.home.copied": {"message": "Address copied."},
|
||||||
"app.skin-site.home.copy": {"message": "Copy"}
|
"app.skin-site.home.copy": {"message": "Copy"},
|
||||||
|
"app.skin-site.admin.title": {"message": "Admin console"},
|
||||||
|
"app.skin-site.admin.back": {"message": "Back to home"},
|
||||||
|
"app.skin-site.admin.no-access": {"message": "You do not have access."},
|
||||||
|
"app.skin-site.admin.no-access-hint": {"message": "Sign in with an administrator account to use this console."},
|
||||||
|
"app.skin-site.admin.group.security": {"message": "Security"},
|
||||||
|
"app.skin-site.admin.group.users": {"message": "Users"},
|
||||||
|
"app.skin-site.admin.group.community": {"message": "Community"},
|
||||||
|
"app.skin-site.admin.group.resources": {"message": "Resources"},
|
||||||
|
"app.skin-site.admin.item.servers": {"message": "Servers"},
|
||||||
|
"app.skin-site.admin.item.summary": {"message": "Summary"},
|
||||||
|
"app.skin-site.admin.item.access-audit": {"message": "Access audit"},
|
||||||
|
"app.skin-site.admin.item.users": {"message": "Users"},
|
||||||
|
"app.skin-site.admin.item.players": {"message": "Players"},
|
||||||
|
"app.skin-site.admin.item.review": {"message": "Player review"},
|
||||||
|
"app.skin-site.admin.item.broadcast": {"message": "Broadcast"},
|
||||||
|
"app.skin-site.admin.item.chat": {"message": "Chat rooms"},
|
||||||
|
"app.skin-site.admin.item.wiki": {"message": "Wiki"},
|
||||||
|
"app.skin-site.admin.item.donations": {"message": "Donations"},
|
||||||
|
"app.skin-site.admin.item.mods": {"message": "Mods"},
|
||||||
|
"app.skin-site.admin.item.launcher": {"message": "Launcher"},
|
||||||
|
"app.skin-site.admin.item.downloads": {"message": "Downloads"},
|
||||||
|
"app.skin-site.home.admin": {"message": "Admin"},
|
||||||
|
"app.skin-site.home.settings": {"message": "Account"},
|
||||||
|
"app.skin-site.home.sign-in": {"message": "Sign in"},
|
||||||
|
"app.skin-site.home.players": {"message": "Players"},
|
||||||
|
"app.skin-site.home.my-players": {"message": "My players"},
|
||||||
|
"app.skin-site.home.no-players": {"message": "No players linked yet."},
|
||||||
|
"app.skin-site.home.loading": {"message": "Loading…"},
|
||||||
|
"app.skin-site.home.error": {"message": "Could not load."},
|
||||||
|
"app.skin-site.home.luck": {"message": "Luck"},
|
||||||
|
"app.skin-site.home.luck-unavailable": {"message": "Unavailable"},
|
||||||
|
"app.skin-site.home.signed-out-title": {"message": "Not signed in"},
|
||||||
|
"app.skin-site.home.signed-out-body": {"message": "Sign in to manage your account and textures."}
|
||||||
}
|
}
|
||||||
@@ -11598,5 +11598,38 @@
|
|||||||
"app.skin-site.home.subtitle": {"message": "星光服务器玩家的衣柜。"},
|
"app.skin-site.home.subtitle": {"message": "星光服务器玩家的衣柜。"},
|
||||||
"app.skin-site.home.address": {"message": "皮肤站地址"},
|
"app.skin-site.home.address": {"message": "皮肤站地址"},
|
||||||
"app.skin-site.home.copied": {"message": "地址已复制。"},
|
"app.skin-site.home.copied": {"message": "地址已复制。"},
|
||||||
"app.skin-site.home.copy": {"message": "复制"}
|
"app.skin-site.home.copy": {"message": "复制"},
|
||||||
|
"app.skin-site.admin.title": {"message": "管理控制台"},
|
||||||
|
"app.skin-site.admin.back": {"message": "返回主页"},
|
||||||
|
"app.skin-site.admin.no-access": {"message": "你没有访问权限。"},
|
||||||
|
"app.skin-site.admin.no-access-hint": {"message": "请使用管理员账号登录后使用本控制台。"},
|
||||||
|
"app.skin-site.admin.group.security": {"message": "系统安全"},
|
||||||
|
"app.skin-site.admin.group.users": {"message": "用户管理"},
|
||||||
|
"app.skin-site.admin.group.community": {"message": "社区管理"},
|
||||||
|
"app.skin-site.admin.group.resources": {"message": "资源分发"},
|
||||||
|
"app.skin-site.admin.item.servers": {"message": "服务器管理"},
|
||||||
|
"app.skin-site.admin.item.summary": {"message": "状态概览"},
|
||||||
|
"app.skin-site.admin.item.access-audit": {"message": "登录与认证审计"},
|
||||||
|
"app.skin-site.admin.item.users": {"message": "用户信息"},
|
||||||
|
"app.skin-site.admin.item.players": {"message": "玩家信息"},
|
||||||
|
"app.skin-site.admin.item.review": {"message": "玩家审核"},
|
||||||
|
"app.skin-site.admin.item.broadcast": {"message": "公告操作"},
|
||||||
|
"app.skin-site.admin.item.chat": {"message": "聊天室管理"},
|
||||||
|
"app.skin-site.admin.item.wiki": {"message": "维基管理"},
|
||||||
|
"app.skin-site.admin.item.donations": {"message": "捐赠管理"},
|
||||||
|
"app.skin-site.admin.item.mods": {"message": "Mod 管理"},
|
||||||
|
"app.skin-site.admin.item.launcher": {"message": "启动器管理"},
|
||||||
|
"app.skin-site.admin.item.downloads": {"message": "下载管理"},
|
||||||
|
"app.skin-site.home.admin": {"message": "管理"},
|
||||||
|
"app.skin-site.home.settings": {"message": "账号"},
|
||||||
|
"app.skin-site.home.sign-in": {"message": "登录"},
|
||||||
|
"app.skin-site.home.players": {"message": "角色"},
|
||||||
|
"app.skin-site.home.my-players": {"message": "我的角色"},
|
||||||
|
"app.skin-site.home.no-players": {"message": "尚未绑定角色。"},
|
||||||
|
"app.skin-site.home.loading": {"message": "加载中…"},
|
||||||
|
"app.skin-site.home.error": {"message": "加载失败。"},
|
||||||
|
"app.skin-site.home.luck": {"message": "幸运值"},
|
||||||
|
"app.skin-site.home.luck-unavailable": {"message": "不可用"},
|
||||||
|
"app.skin-site.home.signed-out-title": {"message": "未登录"},
|
||||||
|
"app.skin-site.home.signed-out-body": {"message": "登录后可管理你的账号与皮肤。"}
|
||||||
}
|
}
|
||||||
Reference in new issue
Block a user