feat(player,admin): 钱包卡片与登录页 UI 优化
- 玩家端我的页:钱包背景图、银行卡式排版、自定义语言下拉与投注规则折叠 - 管理端登录:左右布局、金边半透明卡片、自定义语言选择 - 修复 API promotePlayerToTier1Agent 返回类型导致编译失败
This commit is contained in:
@@ -1,4 +1,5 @@
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, onMounted, onUnmounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import LocaleFlag from './LocaleFlag.vue';
|
||||
import { useAppLocale } from '../composables/useAppLocale';
|
||||
@@ -13,30 +14,62 @@ withDefaults(
|
||||
const { locale } = useI18n();
|
||||
const { locales, setLocale } = useAppLocale();
|
||||
|
||||
async function onChange(code: string) {
|
||||
const open = ref(false);
|
||||
const root = ref<HTMLElement | null>(null);
|
||||
|
||||
const currentLabel = computed(
|
||||
() => locales.find((l) => l.code === locale.value)?.label ?? locale.value,
|
||||
);
|
||||
|
||||
async function pick(code: string) {
|
||||
open.value = false;
|
||||
await setLocale(code);
|
||||
}
|
||||
|
||||
function toggle() {
|
||||
open.value = !open.value;
|
||||
}
|
||||
|
||||
function onDocClick(e: MouseEvent) {
|
||||
if (!root.value?.contains(e.target as Node)) open.value = false;
|
||||
}
|
||||
|
||||
onMounted(() => document.addEventListener('click', onDocClick));
|
||||
onUnmounted(() => document.removeEventListener('click', onDocClick));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div class="locale-switch" :class="{ compact }">
|
||||
<LocaleFlag :locale="locale" :size="compact ? 16 : 18" />
|
||||
<select
|
||||
:value="locale"
|
||||
class="locale-select"
|
||||
:aria-label="compact ? 'Language' : undefined"
|
||||
@change="onChange(($event.target as HTMLSelectElement).value)"
|
||||
>
|
||||
<option v-for="l in locales" :key="l.code" :value="l.code">
|
||||
{{ l.label }}
|
||||
</option>
|
||||
</select>
|
||||
<div ref="root" class="locale-switch" :class="{ compact, open }">
|
||||
<button type="button" class="locale-trigger" :aria-expanded="open" aria-haspopup="listbox" @click.stop="toggle">
|
||||
<LocaleFlag :locale="locale" :size="compact ? 16 : 18" />
|
||||
<span class="locale-label">{{ currentLabel }}</span>
|
||||
<span class="locale-chevron" aria-hidden="true">▾</span>
|
||||
</button>
|
||||
<ul v-show="open" class="locale-menu" role="listbox" :aria-label="compact ? 'Language' : undefined">
|
||||
<li
|
||||
v-for="l in locales"
|
||||
:key="l.code"
|
||||
role="option"
|
||||
:aria-selected="locale === l.code"
|
||||
class="locale-option"
|
||||
:class="{ active: locale === l.code }"
|
||||
@click="pick(l.code)"
|
||||
>
|
||||
<LocaleFlag :locale="l.code" :size="16" />
|
||||
<span>{{ l.label }}</span>
|
||||
</li>
|
||||
</ul>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<style scoped>
|
||||
.locale-switch {
|
||||
display: flex;
|
||||
position: relative;
|
||||
display: inline-flex;
|
||||
}
|
||||
|
||||
.locale-trigger {
|
||||
display: inline-flex;
|
||||
align-items: center;
|
||||
gap: 5px;
|
||||
height: 36px;
|
||||
@@ -44,22 +77,68 @@ async function onChange(code: string) {
|
||||
border: 1px solid var(--border);
|
||||
border-radius: 6px;
|
||||
background: #0d0d0d;
|
||||
color: var(--primary-light);
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
font-family: inherit;
|
||||
cursor: pointer;
|
||||
box-sizing: border-box;
|
||||
}
|
||||
|
||||
.locale-switch.compact {
|
||||
.locale-switch.compact .locale-trigger {
|
||||
height: auto;
|
||||
padding: 2px 5px;
|
||||
min-height: 30px;
|
||||
padding: 4px 6px 4px 5px;
|
||||
}
|
||||
|
||||
.locale-select {
|
||||
background: transparent;
|
||||
color: var(--primary-light);
|
||||
border: none;
|
||||
padding: 2px 2px 2px 0;
|
||||
font-size: 11px;
|
||||
font-weight: 700;
|
||||
outline: none;
|
||||
.locale-label {
|
||||
max-width: 120px;
|
||||
overflow: hidden;
|
||||
text-overflow: ellipsis;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.locale-chevron {
|
||||
font-size: 10px;
|
||||
color: var(--text-muted);
|
||||
transition: transform 0.15s ease;
|
||||
}
|
||||
|
||||
.locale-switch.open .locale-chevron {
|
||||
transform: rotate(180deg);
|
||||
}
|
||||
|
||||
.locale-menu {
|
||||
position: absolute;
|
||||
top: calc(100% + 4px);
|
||||
right: 0;
|
||||
z-index: 50;
|
||||
min-width: 100%;
|
||||
margin: 0;
|
||||
padding: 4px;
|
||||
list-style: none;
|
||||
background: #141414;
|
||||
border: 1px solid var(--border-gold-soft);
|
||||
border-radius: 8px;
|
||||
box-shadow: var(--shadow);
|
||||
}
|
||||
|
||||
.locale-option {
|
||||
display: flex;
|
||||
align-items: center;
|
||||
gap: 8px;
|
||||
padding: 8px 10px;
|
||||
border-radius: 6px;
|
||||
color: var(--text-muted);
|
||||
font-size: 12px;
|
||||
font-weight: 600;
|
||||
cursor: pointer;
|
||||
white-space: nowrap;
|
||||
}
|
||||
|
||||
.locale-option:hover,
|
||||
.locale-option.active {
|
||||
background: rgba(212, 175, 55, 0.1);
|
||||
color: var(--primary-light);
|
||||
}
|
||||
</style>
|
||||
|
||||
Reference in New Issue
Block a user