From 265bc5370a3f97c4237a7bfa6981679031b9e92d Mon Sep 17 00:00:00 2001
From: R2m1liA <15258427350@163.com>
Date: Mon, 22 Sep 2025 16:44:01 +0800
Subject: [PATCH] =?UTF-8?q?feat:=20=E6=90=9C=E7=B4=A2=E9=A1=B5=E6=B7=BB?=
=?UTF-8?q?=E5=8A=A0=E5=88=86=E7=B1=BB=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 使用Element Plus的tab组件搜索页添加了分类功能
- 重构搜索页代码,将搜索结果作为单独的组件
- 调整代码格式,去除部分无用代码
---
app/components/SearchResults.vue | 220 ++++++++++++++++++++++
app/pages/search.vue | 310 ++++++-------------------------
2 files changed, 280 insertions(+), 250 deletions(-)
create mode 100644 app/components/SearchResults.vue
diff --git a/app/components/SearchResults.vue b/app/components/SearchResults.vue
new file mode 100644
index 0000000..1839be9
--- /dev/null
+++ b/app/components/SearchResults.vue
@@ -0,0 +1,220 @@
+
+
+
+
+ {{ getHitTitle(hit.content) }}
+
+ {{ getHitSummary(hit.content) }}
+
+
+ 内容类型:
+ {{ getIndexLabel(hit.type) }}
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/pages/search.vue b/app/pages/search.vue
index 8311224..c26f759 100644
--- a/app/pages/search.vue
+++ b/app/pages/search.vue
@@ -20,84 +20,41 @@
{{ $t('search.search-button') }}
-
- {{ $t('search.results-for', { query: route.query?.query }) }}
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
-
- {{ getHitTitle(hit.content) }}
-
- {{ getHitTitle(hit.content) }}
-
- {{ getHitSummary(hit.content) }}
-
-
- 内容类型:
- {{ getIndexLabel(hit.type) }}
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
import { Search } from '@element-plus/icons-vue';
- import type { SearchHit, SearchSection } from '~/composables/useMeilisearch';
+ // i18n相关
const { t } = useI18n();
+ const { getStrapiLocale } = useLocalizations();
+ const strapiLocale = getStrapiLocale();
+
+ // 路由相关
const route = useRoute();
const router = useRouter();
const localePath = useLocalePath();
- const { getStrapiLocale } = useLocalizations();
- const strapiLocale = getStrapiLocale();
-
- const { search } = useMeilisearch();
-
+ // 搜索相关
const loading = ref(true);
+ const { search } = useMeilisearch();
const keyword = ref('');
- const currentPage = ref(1);
- const pageSize = ref(5);
+ const activeRequestId = ref(0);
+
const sections = ref([]);
- const localeSections = computed(() =>
- sections.value.map((section) => ({
- ...section,
- hits: section.hits.filter(
- (hit) =>
- !hit.locale ||
- String(hit.locale).toLowerCase() === strapiLocale.toLowerCase()
- ),
- }))
- );
+ // 本地化+空Section过滤
const filteredSections = computed(() =>
- localeSections.value.filter((section) => section.hits.length > 0)
+ sections.value
+ .map((section) => ({
+ ...section,
+ hits: section.hits.filter(
+ (hit) =>
+ !hit.locale ||
+ String(hit.locale).toLowerCase() === strapiLocale.toLowerCase()
+ ),
+ }))
+ .filter((section) => section.hits.length > 0)
);
+ // 展平hits
const hits = computed(() =>
filteredSections.value.flatMap((item) =>
item.hits.map((content) => ({ content, type: item.indexUid }))
)
);
- const paginatedHits = computed(() => {
- const start = (currentPage.value - 1) * pageSize.value;
- const end = currentPage.value * pageSize.value;
- return hits.value.slice(start, end);
+
+ // 分类控制
+ const activeTab = ref('all');
+ const resultCount = computed(() => {
+ const map: Record = { all: hits.value.length };
+ for (const hit of hits.value) {
+ map[hit.type] = (map[hit.type] ?? 0) + 1;
+ }
+ return map;
});
- const activeRequestId = ref(0);
-
- const indexLabels = computed>(() => ({
- production: t('search.sections.production'),
- solution: t('search.sections.solution'),
- support: t('search.sections.support'),
- default: t('search.sections.default'),
- }));
-
- const getIndexLabel = (indexUid: string) =>
- indexLabels.value[indexUid] || indexLabels.value.default;
const hasResults = computed(() =>
- localeSections.value.some((section) => section.hits.length > 0)
+ filteredSections.value.some((section) => section.hits.length > 0)
);
- const getHitIdentifier = (hit: SearchHit, index: number) => {
- const candidate = [hit.objectID, hit.documentId, hit.id, hit.slug].find(
- (value) =>
- ['string', 'number'].includes(typeof value) && String(value).length > 0
- );
- return candidate != null ? String(candidate) : String(index);
- };
-
- const getHitTitle = (hit: SearchHit) => {
- const candidate = [
- hit.title,
- hit.name,
- hit.heading,
- hit.documentTitle,
- ].find((value) => typeof value === 'string' && value.trim().length > 0);
- return candidate ? String(candidate) : t('search.untitled');
- };
-
- const getHitSummary = (hit: SearchHit) => {
- const candidate = [
- hit.summary,
- hit.description,
- hit.snippet,
- hit.content,
- hit.text,
- ].find((value) => typeof value === 'string' && value.trim().length > 0);
- return candidate ? String(candidate) : '';
- };
-
- const resolveHitLink = (hit: SearchHit) => {
- if (typeof hit.route === 'string' && hit.route.trim().length > 0) {
- return localePath(hit.route);
- }
-
- const slugCandidate = [hit.slug, hit.documentId, hit.id, hit.objectID].find(
- (value) =>
- ['string', 'number'].includes(typeof value) && String(value).length > 0
- );
-
- if (!slugCandidate) {
- return null;
- }
-
- const slug = String(slugCandidate);
-
- if (hit.indexUid === 'production') {
- return localePath({ path: `/productions/${slug}` });
- }
-
- if (hit.indexUid === 'solution') {
- return localePath({ path: `/solutions/${slug}` });
- }
-
- return null;
- };
-
const navigateToQuery = (value: string) => {
const trimmed = value.trim();
if (!trimmed) return;
@@ -253,7 +150,7 @@
sections.value = results;
}
console.log('hits:', hits.value);
- console.log('paginatedHits:', paginatedHits.value);
+ console.log(resultCount.value);
} catch (error) {
console.error('Failed to perform search', error);
if (requestId === activeRequestId.value) {
@@ -266,32 +163,12 @@
}
};
- function debounce void>(
- fn: T,
- delay: number
- ): (...args: Parameters) => void {
- let timer: ReturnType | null = null;
-
- return function (this: never, ...args: Parameters) {
- if (timer) {
- clearTimeout(timer);
- }
- timer = setTimeout(() => {
- fn.apply(this, args);
- }, delay);
- };
- }
-
const handleClear = () => {
keyword.value = '';
sections.value = [];
router.replace(localePath({ path: '/search' }));
};
- const handleCurrentChange = (page: number) => {
- currentPage.value = page;
- };
-
watch(
() => route.query.query,
(newQuery) => {
@@ -368,73 +245,6 @@
padding: 3rem 0;
}
- .search-results {
- display: flex;
- flex-direction: column;
- gap: 2.5rem;
- }
-
- .search-section {
- display: flex;
- flex-direction: column;
- gap: 1.5rem;
- }
-
- .section-header {
- display: flex;
- align-items: baseline;
- gap: 0.75rem;
- }
-
- .section-title {
- font-size: 1.5rem;
- font-weight: 600;
- color: var(--el-text-color-primary);
- }
-
- .section-count {
- font-size: 0.9rem;
- color: var(--el-text-color-secondary);
- }
-
- .section-results {
- display: grid;
- gap: 1.5rem;
- }
-
- .result-card {
- border-radius: 12px;
- transition: box-shadow 0.2s ease;
- }
-
- .result-card:hover {
- box-shadow: 0 12px 24px rgba(0, 0, 0, 0.06);
- }
-
- .result-title {
- font-size: 1.2rem;
- font-weight: 600;
- color: var(--el-color-primary);
- display: inline-block;
- }
-
- .result-summary {
- font-size: 0.95rem;
- color: var(--el-text-color-regular);
- margin-bottom: 0.5rem;
- line-height: 1.6;
- }
-
- .result-type {
- font-size: 0.8rem;
- color: var(--el-text-color-secondary);
- }
-
- .result-type-name {
- margin-left: 4px;
- color: var(--el-color-primary);
- }
-
@media (max-width: 640px) {
.search-page {
padding: 2rem 1rem;
--
2.49.0