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.

useSearch.ts 2.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114
  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. name: string;
  21. description: string;
  22. summary: string;
  23. matchedField: string;
  24. matchedText: string;
  25. }
  26. /**
  27. * 全局搜索功能钩子
  28. * @returns 搜索相关状态和方法
  29. */
  30. export function useSearch() {
  31. const searchResults = ref<SearchResult[]>([]);
  32. const isLoading = ref(false);
  33. const error = ref<string | null>(null);
  34. const { wrapAsync } = useErrorHandler();
  35. const { locale } = useI18n();
  36. /**
  37. * 搜索产品数据
  38. * @param keyword 搜索关键词
  39. * @returns Promise<void>
  40. */
  41. const searchProducts = async (keyword: string) => {
  42. if (!keyword.trim()) {
  43. searchResults.value = [];
  44. return;
  45. }
  46. isLoading.value = true;
  47. error.value = null;
  48. try {
  49. // 根据当前语言选择对应的JSON文件
  50. const lang = locale.value;
  51. const products = await fetch(`/data/products-${lang}.json`).then(res => res.json()) as Product[];
  52. const results: SearchResult[] = [];
  53. // 搜索数据
  54. products.forEach(product => {
  55. const fields = [
  56. { name: 'title', value: product.title },
  57. { name: 'description', value: product.description },
  58. { name: 'summary', value: product.summary }
  59. ];
  60. fields.forEach(field => {
  61. if (field.value && field.value.toLowerCase().includes(keyword.toLowerCase())) {
  62. results.push({
  63. id: product.id,
  64. title: product.title,
  65. name: product.name,
  66. description: product.description,
  67. summary: product.summary,
  68. matchedField: field.name,
  69. matchedText: field.value
  70. });
  71. }
  72. });
  73. });
  74. // 去重
  75. searchResults.value = Array.from(new Map(results.map(item => [item.id, item])).values());
  76. } catch (err) {
  77. error.value = '搜索失败,请稍后重试';
  78. console.error('Search error:', err);
  79. } finally {
  80. isLoading.value = false;
  81. }
  82. };
  83. /**
  84. * 高亮显示匹配的文本
  85. * @param text 原始文本
  86. * @param keyword 关键词
  87. * @returns 高亮后的HTML字符串
  88. */
  89. const highlightText = (text: string, keyword: string) => {
  90. if (!keyword || !text) return text;
  91. const regex = new RegExp(`(${keyword})`, 'gi');
  92. return text.replace(regex, '<span class="highlight">$1</span>');
  93. };
  94. return {
  95. searchResults,
  96. isLoading,
  97. error,
  98. searchProducts,
  99. highlightText
  100. };
  101. }