195 lines
4.0 KiB
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>
|