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 7.0KB

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