Hanye官网
Вы не можете выбрать более 25 тем Темы должны начинаться с буквы или цифры, могут содержать дефисы(-) и должны содержать не более 35 символов.

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161
  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. import { useRouter } from "vue-router";
  56. // 定义语言代码的类型,应该与 i18n 配置中的一致
  57. type LocaleCode = "zh" | "en" | "ja"; // 你需要根据你的 i18n 配置更新这个类型
  58. const { locale, locales, setLocale } = useI18n();
  59. const router = useRouter();
  60. const currentLocale = computed(() => locale.value);
  61. const isDropdownOpen = ref(false);
  62. const dropdownContainerRef = ref(null);
  63. // 可用语言列表
  64. const availableLocales = computed(() => {
  65. // 确保 locales.value 是一个数组并且包含 code 和 name 属性
  66. return (
  67. (locales.value as Array<{ code: string; name: string }>) || []
  68. ).filter((l) => l.code && l.name);
  69. });
  70. // 当前选中语言的名称
  71. const currentLocaleName = computed(() => {
  72. const current = availableLocales.value.find(
  73. (l: { code: string; name: string }) => l.code === locale.value
  74. );
  75. return current ? current.name : "";
  76. });
  77. /**
  78. * 选择语言并关闭下拉菜单
  79. * @param {string} langCode - 选择的语言代码
  80. */
  81. async function selectLanguage(langCode: string) {
  82. if (currentLocale.value !== langCode) {
  83. try {
  84. // 使用类型断言,确保 langCode 是有效的 LocaleCode
  85. await setLocale(langCode as LocaleCode);
  86. // 获取当前路径(不包含查询参数)
  87. const path = window.location.pathname;
  88. // 获取当前查询参数
  89. const query = window.location.search;
  90. const audiences =
  91. query.length && query.split("?")[1]
  92. ? query
  93. .split("?")[1]
  94. .split("&")
  95. .find((item) => item.startsWith("audiences="))
  96. ?.split("=")[1]
  97. : "";
  98. // 构建新URL,不包含任何查询参数
  99. const newUrl = `${window.location.origin}${path}${
  100. audiences ? `?audiences=${audiences}` : ""
  101. }`;
  102. // 使用新URL替换当前URL并刷新页面
  103. window.location.href = newUrl;
  104. } catch (error) {
  105. console.error("Failed to set locale:", error);
  106. // 这里可以添加用户反馈,例如显示一个错误提示
  107. }
  108. }
  109. closeDropdown();
  110. }
  111. /**
  112. * 切换下拉菜单的显示状态
  113. */
  114. function toggleDropdown() {
  115. isDropdownOpen.value = !isDropdownOpen.value;
  116. }
  117. /**
  118. * 关闭下拉菜单
  119. */
  120. function closeDropdown() {
  121. isDropdownOpen.value = false;
  122. }
  123. /**
  124. * 处理点击外部区域时关闭下拉菜单
  125. */
  126. function handleClickOutside(event: MouseEvent) {
  127. if (
  128. dropdownContainerRef.value &&
  129. !(dropdownContainerRef.value as HTMLElement).contains(
  130. event.target as Node
  131. ) &&
  132. isDropdownOpen.value
  133. ) {
  134. closeDropdown();
  135. }
  136. }
  137. // 监听全局点击事件,用于关闭下拉菜单
  138. onMounted(() => {
  139. document.addEventListener("click", handleClickOutside);
  140. });
  141. onBeforeUnmount(() => {
  142. document.removeEventListener("click", handleClickOutside);
  143. });
  144. </script>