Hanye官网
Du kan inte välja fler än 25 ämnen Ämnen måste starta med en bokstav eller siffra, kan innehålla bindestreck ('-') och vara max 35 tecken långa.

useSearch.ts 3.6KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147
  1. import { ref, computed } from 'vue';
  2. import { useErrorHandler } from './useErrorHandler';
  3. import { useI18n } from 'vue-i18n';
  4. /**
  5. * 产品数据接口
  6. */
  7. interface Product {
  8. id: string;
  9. title: string;
  10. description: string;
  11. summary: string;
  12. [key: string]: any;
  13. }
  14. /**
  15. * 搜索结果项接口
  16. */
  17. interface SearchResult {
  18. id: string;
  19. title: string;
  20. description: string;
  21. summary: string;
  22. matchedField: string;
  23. matchedText: string;
  24. }
  25. /**
  26. * 全局搜索功能钩子
  27. * @returns 搜索相关状态和方法
  28. */
  29. export function useSearch() {
  30. const searchResults = ref<SearchResult[]>([]);
  31. const isLoading = ref(false);
  32. const error = ref<string | null>(null);
  33. const { wrapAsync } = useErrorHandler();
  34. const { locale } = useI18n();
  35. /**
  36. * 搜索产品数据
  37. * @param keyword 搜索关键词
  38. * @returns Promise<void>
  39. */
  40. const searchProducts = async (keyword: string) => {
  41. if (!keyword.trim()) {
  42. searchResults.value = [];
  43. return;
  44. }
  45. isLoading.value = true;
  46. error.value = null;
  47. try {
  48. // 根据当前语言选择对应的JSON文件
  49. const lang = locale.value;
  50. const products = await fetch(`/data/products-${lang}.json`).then(res => res.json()) as Product[];
  51. const results: SearchResult[] = [];
  52. // 搜索数据
  53. products.forEach(product => {
  54. const fields = [
  55. { name: 'title', value: product.title },
  56. { name: 'description', value: product.description },
  57. { name: 'summary', value: product.summary }
  58. ];
  59. fields.forEach(field => {
  60. if (field.value && field.value.toLowerCase().includes(keyword.toLowerCase())) {
  61. results.push({
  62. id: product.id,
  63. title: product.title,
  64. description: product.description,
  65. summary: product.summary,
  66. matchedField: field.name,
  67. matchedText: field.value
  68. });
  69. }
  70. });
  71. });
  72. // 去重
  73. searchResults.value = Array.from(new Map(results.map(item => [item.id, item])).values());
  74. } catch (err) {
  75. error.value = '搜索失败,请稍后重试';
  76. console.error('Search error:', err);
  77. } finally {
  78. isLoading.value = false;
  79. }
  80. };
  81. /**
  82. * 高亮显示匹配的文本
  83. * @param text 原始文本
  84. * @param keyword 关键词
  85. * @returns 高亮后的HTML字符串
  86. */
  87. const highlightText = (text: string, keyword: string) => {
  88. if (!keyword || !text) return text;
  89. const regex = new RegExp(`(${keyword})`, 'gi');
  90. return text.replace(regex, '<span class="highlight">$1</span>');
  91. };
  92. return {
  93. searchResults,
  94. isLoading,
  95. error,
  96. searchProducts,
  97. highlightText
  98. };
  99. }
  100. // 模拟数据,实际项目中应替换为真实API数据
  101. const mockSearchResults: SearchResult[] = [
  102. {
  103. id: '1',
  104. title: '产品一',
  105. description: '这是产品一的详细描述',
  106. summary: '这是产品一的详细描述',
  107. matchedField: 'title',
  108. matchedText: '产品一'
  109. },
  110. {
  111. id: '2',
  112. title: '产品二',
  113. description: '这是产品二的详细描述',
  114. summary: '这是产品二的详细描述',
  115. matchedField: 'title',
  116. matchedText: '产品二'
  117. },
  118. {
  119. id: '3',
  120. title: '如何使用产品?',
  121. description: '详细介绍产品的使用方法',
  122. summary: '详细介绍产品的使用方法',
  123. matchedField: 'description',
  124. matchedText: '使用方法'
  125. },
  126. {
  127. id: '4',
  128. title: '关于我们',
  129. description: '公司简介和历史',
  130. summary: '公司简介和历史',
  131. matchedField: 'description',
  132. matchedText: '公司简介'
  133. }
  134. ];