feat: 多语言支持 https://github.com/vastsa/FileCodeBox/issues/310 https://github.com/vastsa/FileCodeBox/issues/299
This commit is contained in:
@@ -0,0 +1,102 @@
|
||||
<template>
|
||||
<div class="relative">
|
||||
<button
|
||||
@click="toggleDropdown"
|
||||
:class="[
|
||||
'flex items-center space-x-2 px-3 py-2 rounded-lg transition-all duration-200',
|
||||
isDarkMode
|
||||
? 'bg-gray-800/60 hover:bg-gray-700/80 text-gray-300 hover:text-white'
|
||||
: 'bg-gray-100 hover:bg-gray-200 text-gray-700 hover:text-gray-900'
|
||||
]"
|
||||
>
|
||||
<GlobeIcon class="w-4 h-4" />
|
||||
<span class="text-sm font-medium">{{ currentLanguage.name }}</span>
|
||||
<ChevronDownIcon
|
||||
:class="[
|
||||
'w-4 h-4 transition-transform duration-200',
|
||||
{ 'rotate-180': isDropdownOpen }
|
||||
]"
|
||||
/>
|
||||
</button>
|
||||
|
||||
<transition
|
||||
enter-active-class="transition ease-out duration-200"
|
||||
enter-from-class="opacity-0 scale-95"
|
||||
enter-to-class="opacity-100 scale-100"
|
||||
leave-active-class="transition ease-in duration-150"
|
||||
leave-from-class="opacity-100 scale-100"
|
||||
leave-to-class="opacity-0 scale-95"
|
||||
>
|
||||
<div
|
||||
v-if="isDropdownOpen"
|
||||
:class="[
|
||||
'absolute right-0 mt-2 w-32 rounded-lg shadow-lg ring-1 ring-black ring-opacity-5 z-50',
|
||||
isDarkMode
|
||||
? 'bg-gray-800 border border-gray-700'
|
||||
: 'bg-white border border-gray-200'
|
||||
]"
|
||||
>
|
||||
<div class="py-1">
|
||||
<button
|
||||
v-for="language in availableLocales"
|
||||
:key="language.code"
|
||||
@click="switchLanguage(language.code)"
|
||||
:class="[
|
||||
'w-full text-left px-4 py-2 text-sm transition-colors duration-150',
|
||||
currentLocale === language.code
|
||||
? isDarkMode
|
||||
? 'bg-indigo-600 text-white'
|
||||
: 'bg-indigo-50 text-indigo-600'
|
||||
: isDarkMode
|
||||
? 'text-gray-300 hover:bg-gray-700 hover:text-white'
|
||||
: 'text-gray-700 hover:bg-gray-100'
|
||||
]"
|
||||
>
|
||||
{{ language.name }}
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</transition>
|
||||
</div>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, inject, onMounted, onUnmounted } from 'vue'
|
||||
import { useI18n } from 'vue-i18n'
|
||||
import { GlobeIcon, ChevronDownIcon } from 'lucide-vue-next'
|
||||
import { availableLocales, setLocale } from '@/i18n/index'
|
||||
|
||||
const { locale } = useI18n()
|
||||
const isDarkMode = inject('isDarkMode')
|
||||
const isDropdownOpen = ref(false)
|
||||
|
||||
const currentLocale = computed(() => locale.value)
|
||||
const currentLanguage = computed(() => {
|
||||
return availableLocales.find((lang: { code: string; name: string }) => lang.code === currentLocale.value) || availableLocales[0]
|
||||
})
|
||||
|
||||
const toggleDropdown = () => {
|
||||
isDropdownOpen.value = !isDropdownOpen.value
|
||||
}
|
||||
|
||||
const switchLanguage = (langCode: string) => {
|
||||
setLocale(langCode)
|
||||
isDropdownOpen.value = false
|
||||
}
|
||||
|
||||
// 点击外部关闭下拉菜单
|
||||
const handleClickOutside = (event: Event) => {
|
||||
const target = event.target as HTMLElement
|
||||
if (!target.closest('.relative')) {
|
||||
isDropdownOpen.value = false
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(() => {
|
||||
document.addEventListener('click', handleClickOutside)
|
||||
})
|
||||
|
||||
onUnmounted(() => {
|
||||
document.removeEventListener('click', handleClickOutside)
|
||||
})
|
||||
</script>
|
||||
Reference in New Issue
Block a user