Hanye官网
Ви не можете вибрати більше 25 тем Теми мають розпочинатися з літери або цифри, можуть містити дефіси (-) і не повинні перевищувати 35 символів.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137
  1. <template>
  2. <div class="relative inline-block text-left" ref="dropdownContainerRef">
  3. <div
  4. @click="toggleDropdown"
  5. class="flex items-center gap-1 text-white opacity-80 text-sm hover:opacity-100 cursor-pointer py-2 transition-opacity"
  6. >
  7. <i class="icon-i18n mr-1"></i>
  8. <span>{{ currentLocaleName || "Language" }}</span>
  9. <svg
  10. class="h-3 w-3 text-white/60 transition-transform duration-200"
  11. :class="{ 'rotate-180': isDropdownOpen }"
  12. fill="none"
  13. viewBox="0 0 24 24"
  14. stroke="currentColor"
  15. >
  16. <path
  17. stroke-linecap="round"
  18. stroke-linejoin="round"
  19. stroke-width="3"
  20. d="M19 9l-7 7-7-7"
  21. />
  22. </svg>
  23. </div>
  24. <transition name="fade-down">
  25. <div
  26. v-if="isDropdownOpen"
  27. class="absolute right-0 top-full mt-1 w-max min-w-[120px] bg-slate-800/90 backdrop-blur-md rounded-lg shadow-xl p-2 z-10"
  28. >
  29. <ul class="space-y-1">
  30. <li v-for="locale in availableLocales" :key="locale.code">
  31. <button
  32. @click="selectLanguage(locale.code)"
  33. class="block w-full text-left text-base text-gray-200 hover:text-white hover:bg-white/10 transition-all duration-150 rounded px-3 py-1.5"
  34. :class="[
  35. locale.code === currentLocale
  36. ? 'font-bold opacity-100 bg-white/15'
  37. : '',
  38. ]"
  39. >
  40. {{ locale.name }}
  41. </button>
  42. </li>
  43. </ul>
  44. </div>
  45. </transition>
  46. </div>
  47. </template>
  48. <script setup lang="ts">
  49. /**
  50. * 语言切换组件 - 下拉菜单样式
  51. * 支持切换配置的语言,同时支持移动端和桌面端
  52. */
  53. import { ref, computed, onMounted, onBeforeUnmount } from "vue";
  54. import { useI18n } from "#imports";
  55. // 定义语言代码的类型,应该与 i18n 配置中的一致
  56. type LocaleCode = "zh" | "en" | "ja"; // 你需要根据你的 i18n 配置更新这个类型
  57. const { locale, locales, setLocale } = useI18n();
  58. const currentLocale = computed(() => locale.value);
  59. const isDropdownOpen = ref(false);
  60. const dropdownContainerRef = ref(null);
  61. // 可用语言列表
  62. const availableLocales = computed(() => {
  63. // 确保 locales.value 是一个数组并且包含 code 和 name 属性
  64. return (
  65. (locales.value as Array<{ code: string; name: string }>) || []
  66. ).filter((l) => l.code && l.name);
  67. });
  68. // 当前选中语言的名称
  69. const currentLocaleName = computed(() => {
  70. const current = availableLocales.value.find(
  71. (l: { code: string; name: string }) => l.code === locale.value
  72. );
  73. return current ? current.name : "";
  74. });
  75. /**
  76. * 选择语言并关闭下拉菜单
  77. * @param {string} langCode - 选择的语言代码
  78. */
  79. async function selectLanguage(langCode: string) {
  80. if (currentLocale.value !== langCode) {
  81. try {
  82. // 使用类型断言,确保 langCode 是有效的 LocaleCode
  83. await setLocale(langCode as LocaleCode);
  84. window.location.reload();
  85. } catch (error) {
  86. console.error("Failed to set locale:", error);
  87. // 这里可以添加用户反馈,例如显示一个错误提示
  88. }
  89. }
  90. closeDropdown();
  91. }
  92. /**
  93. * 切换下拉菜单的显示状态
  94. */
  95. function toggleDropdown() {
  96. isDropdownOpen.value = !isDropdownOpen.value;
  97. }
  98. /**
  99. * 关闭下拉菜单
  100. */
  101. function closeDropdown() {
  102. isDropdownOpen.value = false;
  103. }
  104. /**
  105. * 处理点击外部区域时关闭下拉菜单
  106. */
  107. function handleClickOutside(event: MouseEvent) {
  108. if (
  109. dropdownContainerRef.value &&
  110. !(dropdownContainerRef.value as HTMLElement).contains(
  111. event.target as Node
  112. ) &&
  113. isDropdownOpen.value
  114. ) {
  115. closeDropdown();
  116. }
  117. }
  118. // 监听全局点击事件,用于关闭下拉菜单
  119. onMounted(() => {
  120. document.addEventListener("click", handleClickOutside);
  121. });
  122. onBeforeUnmount(() => {
  123. document.removeEventListener("click", handleClickOutside);
  124. });
  125. </script>