From c9b5b1fad9ad6b4694671f302afe15c4121d71f8 Mon Sep 17 00:00:00 2001
From: R2m1liA <15258427350@163.com>
Date: Fri, 5 Dec 2025 14:56:24 +0800
Subject: [PATCH 1/4] =?UTF-8?q?feat:=20=E4=B8=BA=E6=90=9C=E7=B4=A2?=
=?UTF-8?q?=E9=A1=B5=E6=A0=8F=E7=9B=AE=E6=B7=BB=E5=8A=A0=E5=9B=BE=E7=89=87?=
=?UTF-8?q?=E7=BC=A9=E7=95=A5=E5=9B=BE=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 图片预览:产品与解决方案栏目添加缩略图功能
- 组件提取:在搜索结果页,将单个搜索结果单独提取为组件SearchResultCard
---
.../pages/search/SearchResultCard.vue | 72 +++++++++++++++++++
app/components/pages/search/SearchResults.vue | 11 +--
server/utils/search-converters.ts | 2 +
shared/types/meilisearch/meili-index.ts | 6 ++
shared/types/views/search-item-view.ts | 3 +
5 files changed, 84 insertions(+), 10 deletions(-)
create mode 100644 app/components/pages/search/SearchResultCard.vue
diff --git a/app/components/pages/search/SearchResultCard.vue b/app/components/pages/search/SearchResultCard.vue
new file mode 100644
index 0000000..3aa76a3
--- /dev/null
+++ b/app/components/pages/search/SearchResultCard.vue
@@ -0,0 +1,72 @@
+
+
+
+
+ {{ item.title }}
+
+ {{ item.summary }}
+
+
+ {{ $t('search.section') }}:
+ {{ typeLabel }}
+
+
+
+
+
+
+
+
+
+
+
+
diff --git a/app/components/pages/search/SearchResults.vue b/app/components/pages/search/SearchResults.vue
index 816f469..1cde91c 100644
--- a/app/components/pages/search/SearchResults.vue
+++ b/app/components/pages/search/SearchResults.vue
@@ -6,16 +6,7 @@
:key="`${hit.type}-${hit.id}`"
:to="localePath(resolveHitLink(hit))"
>
-
- {{ hit.title }}
-
- {{ hit.summary }}
-
-
- {{ $t('search.section') }}:
- {{ getIndexLabel(hit.type) }}
-
-
+
diff --git a/server/utils/search-converters.ts b/server/utils/search-converters.ts
index 2cc6f97..b5a66dc 100644
--- a/server/utils/search-converters.ts
+++ b/server/utils/search-converters.ts
@@ -9,6 +9,7 @@ export const converters: {
type: 'product',
title: item.name,
summary: item.summary,
+ thumbnail: `/api/assets/${item.cover}`,
}),
solutions: (item: MeiliIndexMap['solutions']): SearchItemView => ({
@@ -16,6 +17,7 @@ export const converters: {
type: 'solution',
title: item.title,
summary: item.summary,
+ thumbnail: `/api/assets/${item.cover}`,
}),
questions: (item: MeiliIndexMap['questions']): SearchItemView => ({
diff --git a/shared/types/meilisearch/meili-index.ts b/shared/types/meilisearch/meili-index.ts
index 7b34473..d7a5810 100644
--- a/shared/types/meilisearch/meili-index.ts
+++ b/shared/types/meilisearch/meili-index.ts
@@ -16,6 +16,9 @@ export interface MeiliProductIndex {
/** 产品类型 **/
type: string;
+
+ /** 产品缩略图 **/
+ cover: string;
}
/**
@@ -36,6 +39,9 @@ export interface MeiliSolutionIndex {
/** 解决方案类型 **/
type: string;
+
+ /** 解决方案缩略图 **/
+ cover: string;
}
/**
diff --git a/shared/types/views/search-item-view.ts b/shared/types/views/search-item-view.ts
index e9070f0..594caed 100644
--- a/shared/types/views/search-item-view.ts
+++ b/shared/types/views/search-item-view.ts
@@ -10,4 +10,7 @@ export interface SearchItemView {
/** 条目摘要 **/
summary: string;
+
+ /** 条目预览图 **/
+ thumbnail?: string;
}
--
2.49.0
From 36d24a4740ebb23ac06d70054864a565f0cb491e Mon Sep 17 00:00:00 2001
From: R2m1liA <15258427350@163.com>
Date: Fri, 5 Dec 2025 16:25:57 +0800
Subject: [PATCH 2/4] =?UTF-8?q?fix:=20=E5=8F=AF=E9=80=89=E5=AD=97=E6=AE=B5?=
=?UTF-8?q?=E5=A4=84=E7=90=86?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 为搜索条目中的可选字段进行判断并处理
---
server/utils/search-converters.test.ts | 4 ++++
server/utils/search-converters.ts | 8 ++++----
shared/types/meilisearch/meili-index.ts | 18 +++++++++---------
3 files changed, 17 insertions(+), 13 deletions(-)
diff --git a/server/utils/search-converters.test.ts b/server/utils/search-converters.test.ts
index 95ad2e2..a90c33d 100644
--- a/server/utils/search-converters.test.ts
+++ b/server/utils/search-converters.test.ts
@@ -12,6 +12,7 @@ describe('converters', () => {
summary: 'High efficiency',
description: 'Detailed description',
type: 'pump',
+ cover: 'rand-om__-uuid-1234',
};
const result = converters.products(item);
expect(result).toEqual({
@@ -19,6 +20,7 @@ describe('converters', () => {
type: 'product',
title: 'Hydraulic Pump',
summary: 'High efficiency',
+ thumbnail: '/api/assets/rand-om__-uuid-1234',
});
});
@@ -29,6 +31,7 @@ describe('converters', () => {
summary: 'Effective solution',
content: 'Detailed content',
type: 'Type A',
+ cover: 'rand-om__-uuid-5678',
};
const result = converters.solutions(item);
expect(result).toEqual({
@@ -36,6 +39,7 @@ describe('converters', () => {
type: 'solution',
title: 'Solution A',
summary: 'Effective solution',
+ thumbnail: '/api/assets/rand-om__-uuid-5678',
});
});
diff --git a/server/utils/search-converters.ts b/server/utils/search-converters.ts
index b5a66dc..c334131 100644
--- a/server/utils/search-converters.ts
+++ b/server/utils/search-converters.ts
@@ -8,16 +8,16 @@ export const converters: {
id: item.id,
type: 'product',
title: item.name,
- summary: item.summary,
- thumbnail: `/api/assets/${item.cover}`,
+ summary: item?.summary ?? '',
+ thumbnail: item?.cover ? `/api/assets/${item.cover}` : undefined,
}),
solutions: (item: MeiliIndexMap['solutions']): SearchItemView => ({
id: item.id,
type: 'solution',
title: item.title,
- summary: item.summary,
- thumbnail: `/api/assets/${item.cover}`,
+ summary: item?.summary ?? '',
+ thumbnail: item?.cover ? `/api/assets/${item.cover}` : undefined,
}),
questions: (item: MeiliIndexMap['questions']): SearchItemView => ({
diff --git a/shared/types/meilisearch/meili-index.ts b/shared/types/meilisearch/meili-index.ts
index d7a5810..9555b64 100644
--- a/shared/types/meilisearch/meili-index.ts
+++ b/shared/types/meilisearch/meili-index.ts
@@ -9,16 +9,16 @@ export interface MeiliProductIndex {
name: string;
/** 产品简介 **/
- summary: string;
+ summary?: string;
/** 产品详情 **/
- description: string;
+ description?: string;
/** 产品类型 **/
- type: string;
+ type?: string;
/** 产品缩略图 **/
- cover: string;
+ cover?: string;
}
/**
@@ -32,16 +32,16 @@ export interface MeiliSolutionIndex {
title: string;
/** 解决方案摘要 **/
- summary: string;
+ summary?: string;
/** 解决方案内容 **/
- content: string;
+ content?: string;
/** 解决方案类型 **/
- type: string;
+ type?: string;
/** 解决方案缩略图 **/
- cover: string;
+ cover?: string;
}
/**
@@ -55,7 +55,7 @@ export interface MeiliQuestionIndex {
title: string;
/** 问题内容 **/
- content: string;
+ content?: string;
/** 相关产品 **/
products: string[];
--
2.49.0
From f1398a55457e8e6dc71374f5a6a276109a6f76d8 Mon Sep 17 00:00:00 2001
From: R2m1liA <15258427350@163.com>
Date: Fri, 5 Dec 2025 16:49:09 +0800
Subject: [PATCH 3/4] =?UTF-8?q?feat:=20=E4=B8=BA=E6=90=9C=E7=B4=A2?=
=?UTF-8?q?=E6=9D=A1=E7=9B=AE=E6=B7=BB=E5=8A=A0=E7=BB=86=E5=88=86=E7=B1=BB?=
=?UTF-8?q?=E5=9E=8B?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 类型细分:有原先的四大分类添加细分类型,例如产品(原纸分切机)
- 接口调整:原先的type分类改为sectionType并将type作为细分类型使用
---
.../pages/search/SearchResultCard.vue | 5 ++++-
app/components/pages/search/SearchResults.vue | 17 ++++++++++-------
app/components/pages/search/SearchTabs.vue | 2 +-
server/utils/search-converters.test.ts | 18 ++++++++++++------
server/utils/search-converters.ts | 18 ++++++++++--------
shared/types/meilisearch/meili-index.ts | 6 ++++++
shared/types/views/search-item-view.ts | 7 +++++--
7 files changed, 48 insertions(+), 25 deletions(-)
diff --git a/app/components/pages/search/SearchResultCard.vue b/app/components/pages/search/SearchResultCard.vue
index 3aa76a3..02be23c 100644
--- a/app/components/pages/search/SearchResultCard.vue
+++ b/app/components/pages/search/SearchResultCard.vue
@@ -6,9 +6,12 @@
{{ item.summary }}
-
+
{{ $t('search.section') }}:
{{ typeLabel }}
+ ({{ item.type }})
diff --git a/app/components/pages/search/SearchResults.vue b/app/components/pages/search/SearchResults.vue
index 1cde91c..0414f7b 100644
--- a/app/components/pages/search/SearchResults.vue
+++ b/app/components/pages/search/SearchResults.vue
@@ -3,10 +3,13 @@
-
+
@@ -63,7 +66,7 @@
const items = props.searchItems;
const filteredHits = computed(() => {
if (props.category) {
- return items.filter((item) => item.type === props.category);
+ return items.filter((item) => item.sectionType === props.category);
} else {
return items;
}
@@ -107,19 +110,19 @@
const slug = String(slugCandidate);
- if (item.type === 'product') {
+ if (item.sectionType === 'product') {
return localePath({ path: `/products/${slug}` });
}
- if (item.type === 'solution') {
+ if (item.sectionType === 'solution') {
return localePath({ path: `/solutions/${slug}` });
}
- if (item.type === 'document') {
+ if (item.sectionType === 'document') {
return localePath({ path: `/download/${slug}` });
}
- if (item.type === 'question') {
+ if (item.sectionType === 'question') {
return localePath({ path: `/support/faq`, query: { focus: slug } });
}
diff --git a/app/components/pages/search/SearchTabs.vue b/app/components/pages/search/SearchTabs.vue
index 6ed3e25..1d9e4b5 100644
--- a/app/components/pages/search/SearchTabs.vue
+++ b/app/components/pages/search/SearchTabs.vue
@@ -32,7 +32,7 @@
const resultCount = computed(() => {
const map: Record = { all: props.searchItems.length };
for (const item of props.searchItems) {
- map[item.type] = (map[item.type] ?? 0) + 1;
+ map[item.sectionType] = (map[item.sectionType] ?? 0) + 1;
}
return map;
});
diff --git a/server/utils/search-converters.test.ts b/server/utils/search-converters.test.ts
index a90c33d..db52eeb 100644
--- a/server/utils/search-converters.test.ts
+++ b/server/utils/search-converters.test.ts
@@ -17,9 +17,10 @@ describe('converters', () => {
const result = converters.products(item);
expect(result).toEqual({
id: 1,
- type: 'product',
+ sectionType: 'product',
title: 'Hydraulic Pump',
summary: 'High efficiency',
+ type: 'pump',
thumbnail: '/api/assets/rand-om__-uuid-1234',
});
});
@@ -36,9 +37,10 @@ describe('converters', () => {
const result = converters.solutions(item);
expect(result).toEqual({
id: 1,
- type: 'solution',
+ sectionType: 'solution',
title: 'Solution A',
summary: 'Effective solution',
+ type: 'Type A',
thumbnail: '/api/assets/rand-om__-uuid-5678',
});
});
@@ -51,13 +53,15 @@ describe('converters', () => {
'This is a detailed explanation of how to use the product effectively.',
products: ['Product A'],
product_types: ['Type A'],
+ type: 'Question Type',
};
const result = converters.questions(item);
expect(result).toEqual({
id: 1,
title: 'How to use product?',
- summary: '',
- type: 'question',
+ summary: undefined,
+ type: 'Question Type',
+ sectionType: 'question',
});
});
@@ -68,13 +72,15 @@ describe('converters', () => {
products: ['Product A'],
product_types: ['Type A'],
fileUUID: 'TEST-UUID',
+ type: 'manual',
};
const result = converters.product_documents(item);
expect(result).toEqual({
id: 'TEST-UUID',
title: 'User Manual',
- summary: '',
- type: 'document',
+ summary: undefined,
+ sectionType: 'document',
+ type: 'manual',
});
});
});
diff --git a/server/utils/search-converters.ts b/server/utils/search-converters.ts
index c334131..6560048 100644
--- a/server/utils/search-converters.ts
+++ b/server/utils/search-converters.ts
@@ -6,33 +6,35 @@ export const converters: {
} = {
products: (item: MeiliIndexMap['products']): SearchItemView => ({
id: item.id,
- type: 'product',
+ sectionType: 'product',
title: item.name,
- summary: item?.summary ?? '',
+ summary: item?.summary,
+ type: item?.type,
thumbnail: item?.cover ? `/api/assets/${item.cover}` : undefined,
}),
solutions: (item: MeiliIndexMap['solutions']): SearchItemView => ({
id: item.id,
- type: 'solution',
+ sectionType: 'solution',
title: item.title,
- summary: item?.summary ?? '',
+ summary: item?.summary,
+ type: item?.type,
thumbnail: item?.cover ? `/api/assets/${item.cover}` : undefined,
}),
questions: (item: MeiliIndexMap['questions']): SearchItemView => ({
id: item.id,
- type: 'question',
+ sectionType: 'question',
title: item.title,
- summary: '',
+ type: item?.type,
}),
product_documents: (
item: MeiliIndexMap['product_documents']
): SearchItemView => ({
id: item.fileUUID || item.id,
- type: 'document',
+ sectionType: 'document',
title: item.title,
- summary: '',
+ type: item?.type,
}),
};
diff --git a/shared/types/meilisearch/meili-index.ts b/shared/types/meilisearch/meili-index.ts
index 9555b64..3b6e93d 100644
--- a/shared/types/meilisearch/meili-index.ts
+++ b/shared/types/meilisearch/meili-index.ts
@@ -57,6 +57,9 @@ export interface MeiliQuestionIndex {
/** 问题内容 **/
content?: string;
+ /** 问题类型 **/
+ type?: string;
+
/** 相关产品 **/
products: string[];
@@ -74,6 +77,9 @@ export interface MeiliProductDocumentIndex {
/** 文档标题 **/
title: string;
+ /** 文档类型 **/
+ type?: string;
+
/** 相关产品 **/
products: string[];
diff --git a/shared/types/views/search-item-view.ts b/shared/types/views/search-item-view.ts
index 594caed..1910a03 100644
--- a/shared/types/views/search-item-view.ts
+++ b/shared/types/views/search-item-view.ts
@@ -3,13 +3,16 @@ export interface SearchItemView {
id: number | string;
/** 条目类型 **/
- type: 'product' | 'solution' | 'question' | 'document';
+ sectionType: 'product' | 'solution' | 'question' | 'document';
/** 条目标题 **/
title: string;
/** 条目摘要 **/
- summary: string;
+ summary?: string;
+
+ /** 条目分类 **/
+ type?: string;
/** 条目预览图 **/
thumbnail?: string;
--
2.49.0
From 63cdff9c41f5ad88e535a376a824bf8640dc5cab Mon Sep 17 00:00:00 2001
From: R2m1liA <15258427350@163.com>
Date: Fri, 5 Dec 2025 17:18:48 +0800
Subject: [PATCH 4/4] =?UTF-8?q?feat:=20=E4=B8=BA=E6=96=87=E6=A1=A3?=
=?UTF-8?q?=E5=BA=93=E6=B7=BB=E5=8A=A0=E6=96=87=E6=A1=A3=E7=B1=BB=E5=9E=8B?=
=?UTF-8?q?=E6=98=BE=E7=A4=BA=E5=8A=9F=E8=83=BD?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
- 功能添加:在文档列表中,当未指定文档类型时,在标题右侧显示文档类型
- 查询更改:产品查询添加文档类型查询方法
- mapper更改:productDocumentView添加文档类型
---
app/components/shared/DocumentList.vue | 25 ++++++++++++++++++++++++-
app/pages/support/documents.vue | 8 +++++++-
server/assets/graphql/product.graphql | 7 +++++++
server/mappers/productMapper.test.ts | 25 +++++++++++++++++++++++++
server/mappers/productMapper.ts | 12 ++++++++++++
shared/types/views/product-view.ts | 5 +++++
6 files changed, 80 insertions(+), 2 deletions(-)
diff --git a/app/components/shared/DocumentList.vue b/app/components/shared/DocumentList.vue
index de8669d..36d08fa 100644
--- a/app/components/shared/DocumentList.vue
+++ b/app/components/shared/DocumentList.vue
@@ -7,7 +7,17 @@
@click="handleClick(doc.fileId)"
>
-
{{ doc.title }}
+
+
+ {{ doc.title }}
+
+ |
+
+
+ {{ doc.type.name }}
+
+
+
{{ $t('document-meta.size') }}: {{ formatFileSize(doc.size) }}
@@ -28,6 +38,10 @@
type: Array as () => Array,
default: () => [],
},
+ showCategory: {
+ type: Boolean,
+ default: true,
+ },
});
const localePath = useLocalePath();
@@ -63,6 +77,15 @@
color: var(--el-text-color-secondary);
}
+ .document-title {
+ margin-bottom: 0.5rem;
+ }
+
+ .document-category {
+ font-size: 0.75rem;
+ color: var(--el-text-color-secondary);
+ }
+
.download-button {
margin-left: auto;
}
diff --git a/app/pages/support/documents.vue b/app/pages/support/documents.vue
index 8c0db6f..5bf8fd3 100644
--- a/app/pages/support/documents.vue
+++ b/app/pages/support/documents.vue
@@ -17,7 +17,13 @@
:document-type-options="documentTypeOptions"
/>
-
+
{
filesize: 1000,
filename_download: 'doc1.pdf',
},
+ type: {
+ id: 1,
+ translations: [
+ {
+ id: 1,
+ name: 'manual',
+ },
+ ],
+ },
translations: [
{
id: 1,
@@ -363,6 +372,10 @@ describe('toProductDocumentView', () => {
fileId: 'rand-om__-uuid-1234',
filename: 'doc1.pdf',
title: 'Document Title 1',
+ type: {
+ id: '1',
+ name: 'manual',
+ },
size: 1000,
url: '/api/assets/rand-om__-uuid-1234',
},
@@ -391,6 +404,10 @@ describe('toProductDocumentView', () => {
fileId: 'rand-om__-uuid-1234',
filename: 'doc1.pdf',
title: '',
+ type: {
+ id: '-1',
+ name: '',
+ },
size: 1000,
url: '/api/assets/rand-om__-uuid-1234',
},
@@ -413,6 +430,10 @@ describe('toProductDocumentView', () => {
filename: '',
title: '',
size: 0,
+ type: {
+ id: '-1',
+ name: '',
+ },
url: '',
},
{
@@ -421,6 +442,10 @@ describe('toProductDocumentView', () => {
filename: '',
title: '',
size: 0,
+ type: {
+ id: '-1',
+ name: '',
+ },
url: '',
},
]);
diff --git a/server/mappers/productMapper.ts b/server/mappers/productMapper.ts
index 2896d96..453b85a 100644
--- a/server/mappers/productMapper.ts
+++ b/server/mappers/productMapper.ts
@@ -1,4 +1,5 @@
import { isObject } from '../../server/utils/object';
+import { toDocumentTypeView } from './documentMapper';
/**
* 将 Directus 返回的 ProductImage 数据转换为 ProductImageView 视图模型
@@ -161,6 +162,10 @@ export function toProductDocumentView(
size: 0,
title: '',
url: '',
+ type: {
+ id: '-1',
+ name: '',
+ },
} satisfies ProductDocumentView;
}
@@ -173,6 +178,10 @@ export function toProductDocumentView(
size: 0,
title: '',
url: '',
+ type: {
+ id: '-1',
+ name: '',
+ },
} satisfies ProductDocumentView;
}
@@ -184,6 +193,8 @@ export function toProductDocumentView(
const trans = document.translations?.[0];
+ const typeView = toDocumentTypeView(document.type ?? null);
+
return {
id: item.id.toString(),
fileId: file?.id ?? '',
@@ -191,6 +202,7 @@ export function toProductDocumentView(
size: file?.filesize ?? 0,
title: trans?.title ?? '',
url: url,
+ type: typeView,
} satisfies ProductDocumentView;
});
}
diff --git a/shared/types/views/product-view.ts b/shared/types/views/product-view.ts
index 7407ddc..0c2a08a 100644
--- a/shared/types/views/product-view.ts
+++ b/shared/types/views/product-view.ts
@@ -1,3 +1,5 @@
+import type { DocumentTypeView } from './document-list-view';
+
/**
* 产品图片视图模型
* 用于产品详情页(/products/[slug])中的产品图片数据结构
@@ -73,6 +75,9 @@ export interface ProductDocumentView {
/** 文档大小 **/
size: number;
+ /** 文档类型 **/
+ type: DocumentTypeView;
+
/** 文档链接 **/
url: string;
}
--
2.49.0