Hanye官网
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

TheFooter.vue 5.9KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192
  1. <template>
  2. <footer
  3. class="text-white py-8 w-full relative bg-stone-950 border-t border-zinc-800 overflow-hidden"
  4. >
  5. <div class="max-w-screen-2xl mx-auto px-4 sm:px-6 lg:px-10">
  6. <div
  7. class="grid grid-cols-1 md:grid-cols-2 sm:grid-cols-1 lg:grid-cols-4 gap-8 lg:py-14"
  8. >
  9. <!-- Logo & 描述 -->
  10. <div
  11. class="flex flex-col lg:items-start :lg:justify-start sm:items-center text-center lg:text-left col-span-2 lg:col-span-1"
  12. >
  13. <h3 class="mb-4">
  14. <i class="icon-brand text-white text-2xl"></i>
  15. </h3>
  16. <LanguageSwitcher class="mb-8 hidden lg:block" />
  17. <p class="text-white opacity-60 text-xs">
  18. &copy; {{ new Date().getFullYear() }} Hanye. All rights reserved.
  19. </p>
  20. </div>
  21. <!-- 产品分类链接 -->
  22. <div class="hidden lg:block">
  23. <h3
  24. class="justify-start text-zinc-300 text-xl font-normal leading-snug mb-4"
  25. >
  26. {{ t("common.footer.productsLinks.title") }}
  27. </h3>
  28. <ul class="space-y-4">
  29. <li v-for="item in menuProductsItems" :key="item.path">
  30. <NuxtLink
  31. :to="item.path"
  32. class="text-zinc-500 text-sm font-normal hover:text-white transition"
  33. >
  34. {{ t(item.label) }}
  35. </NuxtLink>
  36. </li>
  37. </ul>
  38. </div>
  39. <!-- 网站快捷链接 -->
  40. <div class="hidden lg:block">
  41. <h3
  42. class="justify-start text-zinc-300 text-xl font-normal leading-snug mb-4"
  43. >
  44. {{ t("common.footer.websiteLinks.title") }}
  45. </h3>
  46. <ul class="space-y-4">
  47. <li v-for="item in menuWebsiteItems" :key="item.path">
  48. <NuxtLink
  49. :to="item.path"
  50. class="text-zinc-500 text-sm font-normal hover:text-white transition"
  51. >
  52. {{ t(item.label) }}
  53. </NuxtLink>
  54. </li>
  55. </ul>
  56. </div>
  57. <!-- 法律与支持 -->
  58. <div class="hidden lg:block">
  59. <h3
  60. class="justify-start text-zinc-300 text-xl font-normal leading-snug mb-4"
  61. >
  62. {{ t("common.footer.legalAndSupport.title") }}
  63. </h3>
  64. <ul class="space-y-4">
  65. <li v-for="item in menuLegalAndSupportItems" :key="item.path">
  66. <NuxtLink
  67. :to="item.path"
  68. class="text-zinc-500 text-sm font-normal hover:text-white transition"
  69. >
  70. {{ t(item.label) }}
  71. </NuxtLink>
  72. </li>
  73. </ul>
  74. </div>
  75. </div>
  76. </div>
  77. <!-- 返回顶部按钮 -->
  78. <BackToTop />
  79. </footer>
  80. </template>
  81. <script setup lang="ts">
  82. /**
  83. * 页脚组件
  84. * 包含网站导航、联系信息和版权信息
  85. */
  86. import { useI18n } from "vue-i18n";
  87. const { locale, t } = useI18n();
  88. const config = useRuntimeConfig();
  89. const defaultLocale = config.public.i18n?.defaultLocale || "zh";
  90. // 获取产品分类数据
  91. const { data: categoryResponse } = await useAsyncData(
  92. `footer-categories-${locale.value}`,
  93. async () => {
  94. try {
  95. // 使用queryCollection从content目录获取数据
  96. const content = await queryCollection("content")
  97. .where("path", "LIKE", `/categories/${locale.value}/%`)
  98. .all();
  99. if (!content || !Array.isArray(content)) {
  100. console.log("No category content found for footer");
  101. return [];
  102. }
  103. // 转换为需要的格式
  104. return content
  105. .map((item: any) => {
  106. // 从路径中提取ID - 文件名就是ID
  107. const pathParts = item.path?.split("/");
  108. const idFile = pathParts?.[pathParts.length - 1] || "";
  109. // 如果ID在元数据中有提供,优先使用元数据中的ID
  110. const id = item.id
  111. ? parseInt(item.id)
  112. : parseInt(idFile.replace(".md", "")) || 0;
  113. return {
  114. title: item.title || "",
  115. id: id,
  116. };
  117. })
  118. .sort((a, b) => (a.id || 0) - (b.id || 0));
  119. } catch (error) {
  120. console.error("Error loading category data for footer:", error);
  121. return [];
  122. }
  123. }
  124. );
  125. // 使用计算属性处理产品分类数据
  126. const productCategories = computed(() => {
  127. return categoryResponse.value || [];
  128. });
  129. // 导航菜单项
  130. const menuProductsItems = computed(() => {
  131. // 构建路径前缀
  132. const prefix = locale.value === defaultLocale ? "" : `/${locale.value}`;
  133. // 使用修改后的产品分类数据
  134. return productCategories.value.map((category: any) => ({
  135. label: category.title,
  136. path: `${prefix}/products?category=${encodeURIComponent(category.title)}`,
  137. }));
  138. });
  139. const menuWebsiteItems = computed(() => [
  140. {
  141. label: "common.footer.websiteLinks.home",
  142. path: locale.value === defaultLocale ? "/" : `/${locale.value}`,
  143. },
  144. {
  145. label: "common.footer.websiteLinks.products",
  146. path:
  147. locale.value === defaultLocale
  148. ? "/products"
  149. : `/${locale.value}/products`,
  150. },
  151. {
  152. label: "common.footer.websiteLinks.faq",
  153. path: locale.value === defaultLocale ? "/faq" : `/${locale.value}/faq`,
  154. },
  155. {
  156. label: "common.footer.websiteLinks.about",
  157. path: locale.value === defaultLocale ? "/about" : `/${locale.value}/about`,
  158. },
  159. {
  160. label: "common.footer.websiteLinks.contact",
  161. path:
  162. locale.value === defaultLocale ? "/contact" : `/${locale.value}/contact`,
  163. },
  164. ]);
  165. const menuLegalAndSupportItems = computed(() => [
  166. {
  167. label: "common.footer.legalAndSupport.support",
  168. path:
  169. locale.value === defaultLocale ? "/support" : `/${locale.value}/support`,
  170. },
  171. {
  172. label: "common.footer.legalAndSupport.privacy",
  173. path:
  174. locale.value === defaultLocale ? "/privacy" : `/${locale.value}/privacy`,
  175. },
  176. {
  177. label: "common.footer.legalAndSupport.terms",
  178. path: locale.value === defaultLocale ? "/terms" : `/${locale.value}/terms`,
  179. },
  180. ]);
  181. </script>