website-client-Nigeria/src/components/common/LanguageSwitcher.vue

195 lines
4.0 KiB
Vue

<template>
<div class="language-switcher">
<div class="language-dropdown" @click="toggleDropdown" ref="dropdownRef">
<div class="current-language">
<span class="language-name">{{ currentLanguageName }}</span>
<i class="dropdown-arrow" :class="{ 'rotated': isDropdownOpen }"></i>
</div>
<div class="language-options" v-if="isDropdownOpen">
<div
v-for="lang in availableLanguages"
:key="lang.code"
class="language-option"
:class="{ 'active': lang.code === currentLocale }"
@click.stop="selectLanguage(lang.code)"
>
<span class="language-code">{{ lang.code.toUpperCase() }}</span>
<span class="language-name">{{ lang.name }}</span>
</div>
</div>
</div>
</div>
</template>
<script setup>
const { locale } = useI18n()
const { switchLanguage, getCurrentLanguageName, getLanguageName, isLanguageAvailable } = useLanguageSwitch()
// 响应式状态
const isDropdownOpen = ref(false)
const dropdownRef = ref(null)
// 计算属性
const currentLocale = computed(() => locale.value)
const currentLanguageName = computed(() => getCurrentLanguageName())
const availableLanguages = computed(() => [
{ code: 'en', name: 'English' },
{ code: 'cn', name: '简体中文' }
])
// 切换下拉菜单
const toggleDropdown = () => {
isDropdownOpen.value = !isDropdownOpen.value
}
// 选择语言
const selectLanguage = async (langCode) => {
if (!isLanguageAvailable(langCode) || langCode === currentLocale.value) {
return
}
try {
await switchLanguage(langCode, useRouter())
isDropdownOpen.value = false
} catch (error) {
console.error('语言切换失败:', error)
}
}
// 点击外部关闭下拉菜单
const handleClickOutside = (event) => {
if (dropdownRef.value && !dropdownRef.value.contains(event.target)) {
isDropdownOpen.value = false
}
}
// 生命周期
onMounted(() => {
document.addEventListener('click', handleClickOutside)
})
onUnmounted(() => {
document.removeEventListener('click', handleClickOutside)
})
</script>
<style scoped>
.language-switcher {
position: relative;
display: inline-block;
}
.language-dropdown {
cursor: pointer;
user-select: none;
position: relative;
}
.current-language {
display: flex;
align-items: center;
gap: 8px;
padding: 8px 12px;
border-radius: 6px;
background: rgba(255, 255, 255, 0.1);
border: 1px solid rgba(255, 255, 255, 0.2);
transition: all 0.3s ease;
}
.current-language:hover {
background: rgba(255, 255, 255, 0.2);
border-color: rgba(255, 255, 255, 0.3);
}
.language-name {
font-size: 14px;
font-weight: 500;
color: #fff;
}
.dropdown-arrow {
font-size: 10px;
color: #fff;
transition: transform 0.3s ease;
}
.dropdown-arrow.rotated {
transform: rotate(180deg);
}
.language-options {
position: absolute;
top: 100%;
right: 0;
margin-top: 4px;
background: #fff;
border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
border: 1px solid rgba(0, 0, 0, 0.1);
overflow: hidden;
z-index: 1000;
min-width: 140px;
}
.language-option {
display: flex;
align-items: center;
gap: 12px;
padding: 12px 16px;
cursor: pointer;
transition: background-color 0.2s ease;
border-bottom: 1px solid rgba(0, 0, 0, 0.05);
}
.language-option:last-child {
border-bottom: none;
}
.language-option:hover {
background-color: #f8f9fa;
}
.language-option.active {
background-color: #e3f2fd;
color: #1976d2;
}
.language-option.active .language-code,
.language-option.active .language-name {
color: #1976d2;
font-weight: 600;
}
.language-code {
font-size: 12px;
font-weight: 600;
color: #666;
min-width: 24px;
}
.language-option .language-name {
font-size: 14px;
color: #333;
font-weight: 500;
}
/* 响应式设计 */
@media (max-width: 768px) {
.language-options {
right: auto;
left: 0;
min-width: 120px;
}
.current-language {
padding: 6px 10px;
}
.language-name {
font-size: 13px;
}
}
</style>