feat: 为文档库/相关问题页面添加类型筛选功能 #92

Manually merged
remilia merged 10 commits from feat/support-category into master 2025-12-03 18:00:13 +08:00
5 changed files with 167 additions and 2 deletions
Showing only changes of commit f4ec82a150 - Show all commits

View File

@ -1,6 +1,13 @@
query GetQuestionList($locale: String!) {
questions(filter: { status: { _eq: "published" } }) {
id
type {
id
translations(filter: { languages_code: { code: { _eq: $locale } } }) {
id
name
}
}
translations(filter: { languages_code: { code: { _eq: $locale } } }) {
id
title

View File

@ -1,5 +1,51 @@
import { describe, expect, test } from 'vitest';
import { toProductQuestionView, toQuestionListView } from './questionMapper';
import {
toProductQuestionView,
toQuestionListView,
toQuestionTypeView,
} from './questionMapper';
/**
* 单元测试: toQuestionTypeView
*/
describe('toQuestionTypeView', () => {
const baseData: QuestionType = {
id: 1,
translations: [{ id: 1, name: 'Type Name' }],
};
test('convert raw data to QuestionTypeView correctly', () => {
const rawData: QuestionType = {
...baseData,
};
expect(toQuestionTypeView(rawData)).toEqual({
id: '1',
type: 'Type Name',
});
});
test('convert raw data with missing translations', () => {
const rawData: QuestionType = {
...baseData,
translations: [],
};
expect(toQuestionTypeView(rawData)).toEqual({
id: '1',
type: '',
});
});
test('convert null input to default QuestionTypeView', () => {
const rawData: QuestionType | null = null;
expect(toQuestionTypeView(rawData)).toEqual({
id: '',
type: '',
});
});
});
/**
* 单元测试: toProductQuestionView
@ -43,6 +89,10 @@ describe('toProductQuestionView', () => {
describe('toQuestionListView', () => {
const baseData: Question = {
id: 1,
type: {
id: 1,
translations: [{ id: 1, name: 'Type Name' }],
},
translations: [
{ id: 1, title: 'Question Title', content: 'Question Answer' },
],
@ -68,6 +118,10 @@ describe('toQuestionListView', () => {
expect(toQuestionListView(rawData)).toEqual({
id: '1',
type: {
id: '1',
type: 'Type Name',
},
title: 'Question Title',
content: 'Question Answer',
products: [
@ -104,6 +158,10 @@ describe('toQuestionListView', () => {
expect(toQuestionListView(rawData)).toEqual({
id: '1',
type: {
id: '1',
type: 'Type Name',
},
title: '',
content: '',
products: [

View File

@ -1,5 +1,31 @@
import { isObject } from '../../server/utils/object';
/**
* 将 Directus 返回的 QuestionType 类型转换为 QuestionTypeView 视图模型
*
* @param raw: 原始的 QuestionType 数据
* @returns 转换后的 QuestionTypeView 对象
*
* @example
* const view = toQuestionTypeView(rawQuestionType);
*/
export function toQuestionTypeView(
raw: QuestionType | string | null
): QuestionTypeView {
if (typeof raw === 'string' || raw === null) {
return {
id: '',
type: '',
} satisfies QuestionTypeView;
}
const trans = raw.translations?.[0];
return {
id: raw.id.toString(),
type: trans?.name ?? '',
};
}
/**
* 将 Directus 返回的 Question 数据转换为 ProductQuestionView 视图模型
*
@ -31,6 +57,8 @@ export function toProductQuestionView(raw: Question): ProductQuestionView {
export function toQuestionListView(raw: Question): QuestionListView {
const trans = raw.translations?.[0];
const type = toQuestionTypeView(raw.type ?? null);
const related_products: QuestionListProduct[] = (raw.products ?? [])
.filter(isObject<ProductsQuestion>)
.map((item) => item.products_id)
@ -57,6 +85,7 @@ export function toQuestionListView(raw: Question): QuestionListView {
return {
id: raw.id.toString(),
type: type,
title: trans?.title ?? '',
content: trans?.content ?? '',
products: related_products,

View File

@ -7,6 +7,18 @@ export interface QuestionListProductType {
name: string;
}
/**
* 问题类型
* 用于在常见问题列表中提供产品筛选功能
*/
export interface QuestionTypeView {
/** 唯一标识符 **/
id: string;
/** 类型名 **/
type: string;
}
/**
* 问题关联产品模型
* 用于在常见问题列表中提供产品筛选功能
@ -25,6 +37,9 @@ export interface QuestionListView {
/** 唯一标识符 **/
id: string;
/** 问题类型 **/
type: QuestionTypeView;
/** 问题标题 **/
title: string;