From 98f978484c484261336374ac141d33e14a6f1995 Mon Sep 17 00:00:00 2001 From: R2m1liA <15258427350@163.com> Date: Wed, 15 Oct 2025 16:48:38 +0800 Subject: [PATCH] =?UTF-8?q?feat:=20directus=E8=A7=86=E5=9B=BE=E4=B8=8E?= =?UTF-8?q?=E8=BD=AC=E6=8D=A2=E5=87=BD=E6=95=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - views: 用于前端渲染的视图模型 - mapper: 用于视图模型转换的转换函数 - utils: 相关工具函数 --- app/models/mappers/documentMapper.ts | 29 +++++ app/models/mappers/productMapper.ts | 134 +++++++++++++++++++++++ app/models/mappers/questionMapper.ts | 18 +++ app/models/utils/object.ts | 30 +++++ app/models/views/ProductDocumentView.ts | 20 ++++ app/models/views/ProductListView.ts | 20 ++++ app/models/views/ProductSpecGroupView.ts | 29 +++++ app/models/views/ProductView.ts | 38 +++++++ app/models/views/QuestionView.ts | 14 +++ 9 files changed, 332 insertions(+) create mode 100644 app/models/mappers/documentMapper.ts create mode 100644 app/models/mappers/productMapper.ts create mode 100644 app/models/mappers/questionMapper.ts create mode 100644 app/models/utils/object.ts create mode 100644 app/models/views/ProductDocumentView.ts create mode 100644 app/models/views/ProductListView.ts create mode 100644 app/models/views/ProductSpecGroupView.ts create mode 100644 app/models/views/ProductView.ts create mode 100644 app/models/views/QuestionView.ts diff --git a/app/models/mappers/documentMapper.ts b/app/models/mappers/documentMapper.ts new file mode 100644 index 0000000..01f08ab --- /dev/null +++ b/app/models/mappers/documentMapper.ts @@ -0,0 +1,29 @@ +/** + * 将 Directus 返回的 Document 数据转换为 DocumentView 视图模型 + * + * @param raw: 原始的 Document 数据 + * @returns 转换后的 DocumentView 对象 + * + * @example + * const view = toDocumentView(rawDocument); + */ +export function toDocumentView(raw: ProductDocument): ProductDocumentView { + const trans = raw.translations?.[0] ?? { + title: '', + }; + + const fileId = typeof raw.file === 'string' ? raw.file : raw.file?.id; + const file = raw.file as DirectusFile; + + const { getFileUrl } = useDirectusFiles(); + + const url = getFileUrl(fileId); + + return { + id: raw.id, + filename: file.filename_download, + title: trans.title, + url: url, + size: file.filesize, + }; +} diff --git a/app/models/mappers/productMapper.ts b/app/models/mappers/productMapper.ts new file mode 100644 index 0000000..0ef72aa --- /dev/null +++ b/app/models/mappers/productMapper.ts @@ -0,0 +1,134 @@ +/** + * 将 Directus返回的 Product 数据转换为 ProductListView 视图模型 + * + * @param raw: 原始的 Product 数据 + * @returns 转换后的 ProductListView 对象 + * + * @example + * const view = toProductListView(rawProduct); + */ +export function toProductListView(raw: Product): ProductListView { + const trans = raw.translations?.[0] ?? { name: '', summary: '' }; + + return { + id: raw.id, + product_type: + typeof raw.product_type === 'string' + ? raw.product_type + : typeof raw.product_type === 'object' && raw.product_type + ? raw.product_type.translations[0].name || '' + : '', + name: trans.name, + summary: trans.summary, + cover: raw.cover.toString(), + }; +} + +/** + * 将 Directus 返回的 ProductSpecGroup 数据转换为 ProductSpecGroupView 视图模型 + * + * @param raw: 原始的 ProductSpecGroup 数据 + * @returns 转换后的 ProductSpecGroupView 对象 + * + * @example + * const view = toProductSpecGroupView(rawSpecGroup); + */ +export function toProductSpecGroupView( + raw: ProductSpecGroup +): ProductSpecGroupView { + const trans = raw.translations?.[0] ?? { + name: '', + }; + + return { + id: raw.id, + name: trans.name, + specs: raw.specs + .filter(isObject) + .map((item) => toProductSpecView(item)), + }; +} + +/** + * 将 Directus 返回的 ProductSpec 数据转换为 ProductSpecView 视图模型 + * + * @param raw: 原始的 ProductSpec 数据 + * @returns 转换后的 ProductSpecView 对象 + * + * @example + * const view = toProductSpecView(rawSpecGroup); + */ +export function toProductSpecView(raw: ProductSpec): ProductSpecView { + const trans = raw.translations?.[0] ?? { + key: '', + }; + + return { + id: raw.id, + key: trans.key, + value: raw.value, + }; +} + +/** + * 将 Directus 返回的 Product 数据转换为 ProductView 视图模型 + * + * @param raw: 原始的 Product 数据 + * @returns 转换后的 ProductView 对象 + * + * @example + * const view = toProductView(rawProduct); + */ +export function toProductView(raw: Product): ProductView { + const trans = raw.translations?.[0] ?? { + name: '', + summary: '', + description: '', + }; + + const images = (raw.images ?? []) + .filter(isObject) + .map((item) => item.product_images_id) + .filter(isObject) + .map((item) => { + return { + id: item.id, + image: item.image.toString(), + caption: item.translations?.[0]?.caption || '', + }; + }); + + const specs = (raw.specs ?? []) + .filter(isObject) + .map((item) => toProductSpecGroupView(item)); + + const faqs = (raw.faqs ?? []) + .filter(isObject) + .map((item) => item.questions_id) + .filter(isObject) + .map((item) => toQuestionView(item)); + + const documents = (raw.documents ?? []) + .filter(isObject) + .map((item) => item.product_documents_id) + .filter(isObject) + .map((item) => toDocumentView(item)); + + return { + id: raw.id, + + product_type: + typeof raw.product_type === 'string' + ? raw.product_type + : typeof raw.product_type === 'object' && raw.product_type + ? raw.product_type.translations[0].name || '' + : '', + name: trans.name, + summary: trans.summary, + images: images, + description: trans.description, + specs: specs, + faqs: faqs, + documents: documents, + }; +} diff --git a/app/models/mappers/questionMapper.ts b/app/models/mappers/questionMapper.ts new file mode 100644 index 0000000..45ec955 --- /dev/null +++ b/app/models/mappers/questionMapper.ts @@ -0,0 +1,18 @@ +/** + * 将 Directus 返回的 Question 数据转换为 QuestionView 视图模型 + * + * @param raw: 原始的 Question 数据 + * @returns 转换后的 QuestionView 对象 + * + * @example + * const view = toQuestionView(rawQuestion); + */ +export function toQuestionView(raw: Question): QuestionView { + const trans = raw.translations?.[0] ?? { title: '', content: '' }; + + return { + id: raw.id, + title: trans.title, + content: trans.content, + }; +} diff --git a/app/models/utils/object.ts b/app/models/utils/object.ts new file mode 100644 index 0000000..37ef512 --- /dev/null +++ b/app/models/utils/object.ts @@ -0,0 +1,30 @@ +/** + * 判断某一值是否为非null对象 + * + * @template T 泛型类型,用于推断目标对象的类型 + * @param value: 需要判断的值 + * @returns 如果值是非null对象则返回true,否则返回false + * + * @example + * if (isObject(value)) value.id + */ +export const isObject = (value: unknown): value is T => + typeof value === 'object' && value !== null; + +/** + * 判断某一值是否为非null对象组成的数组 + * + * @template T 泛型类型,用于推断目标对象的类型 + * @param value: 需要判断的值 + * @returns 如果值是非null对象组成的数组则返回true,否则返回false + * + * @example + * const data: unknown = [{ id: 1 }, { id: 2 }]; + * if (isArrayOfObject)<{ id: number }>(data) { + * // TypeScript 知道 data 是 { id: number }[] 类型 + * console.log(data[0].id); + * } + */ +export const isArrayOfObject = (arr: unknown): arr is T[] => { + return Array.isArray(arr) && arr.every(isObject); +}; diff --git a/app/models/views/ProductDocumentView.ts b/app/models/views/ProductDocumentView.ts new file mode 100644 index 0000000..3c72533 --- /dev/null +++ b/app/models/views/ProductDocumentView.ts @@ -0,0 +1,20 @@ +/** + * 文档视图模型 + * 用于文档页(/support/documents)渲染的数据结构 + */ +export interface ProductDocumentView { + /** 唯一标识符 **/ + id: number; + + /** 文件名 **/ + filename: string; + + /** 文档标题 **/ + title: string; + + /** 文档大小 **/ + size: number; + + /** 文档链接 **/ + url: string; +} diff --git a/app/models/views/ProductListView.ts b/app/models/views/ProductListView.ts new file mode 100644 index 0000000..14912fe --- /dev/null +++ b/app/models/views/ProductListView.ts @@ -0,0 +1,20 @@ +/** + * 产品列表视图模型 + * 用于产品列表(/products)渲染的数据结构 + */ +export interface ProductListView { + /** 唯一标识符 **/ + id: number; + + /** 产品名称 **/ + name: string; + + /** 产品简介 **/ + summary: string; + + /** 产品类型 **/ + product_type: string; + + /** 产品封面(图片的id) **/ + cover: string; +} diff --git a/app/models/views/ProductSpecGroupView.ts b/app/models/views/ProductSpecGroupView.ts new file mode 100644 index 0000000..361fa71 --- /dev/null +++ b/app/models/views/ProductSpecGroupView.ts @@ -0,0 +1,29 @@ +/** + * 产品规格模型 + * 用于产品规格渲染的数据结构 + */ +export interface ProductSpecView { + /** 唯一标识符 **/ + id: number; + + /** 规格名称 **/ + key: string; + + /** 规格值 **/ + value: string; +} + +/** + * 产品规格表模型 + * 用于产品规格表渲染的数据结构 + */ +export interface ProductSpecGroupView { + /** 唯一标识符 **/ + id: number; + + /** 规格组名称 **/ + name: string; + + /** 规格组 **/ + specs: ProductSpecView[]; +} diff --git a/app/models/views/ProductView.ts b/app/models/views/ProductView.ts new file mode 100644 index 0000000..f91e061 --- /dev/null +++ b/app/models/views/ProductView.ts @@ -0,0 +1,38 @@ +interface ImageView { + id: number; + image: string; + caption: string; +} + +/** + * 产品视图模型 + * 用于产品详情页(/products/[slug])渲染的数据结构 + */ +export interface ProductView { + /** 唯一标识符 **/ + id: number; + + /** 产品名称 **/ + name: string; + + /** 产品简介 **/ + summary: string; + + /** 产品类型 **/ + product_type: string; + + /** 产品图片 **/ + images: ImageView[]; + + /** 产品详情 **/ + description: string; + + /** 产品规格 **/ + specs: ProductSpecGroupView[]; + + /** 产品常见问题 **/ + faqs: QuestionView[]; + + /** 产品文档 **/ + documents: ProductDocumentView[]; +} diff --git a/app/models/views/QuestionView.ts b/app/models/views/QuestionView.ts new file mode 100644 index 0000000..310afd0 --- /dev/null +++ b/app/models/views/QuestionView.ts @@ -0,0 +1,14 @@ +/** + * 常见问题视图模型 + * 用于常见问题(/support/faqs)渲染的数据结构 + */ +export interface QuestionView { + /** 唯一标识符 **/ + id: number; + + /** 问题标题 **/ + title: string; + + /** 问题内容 **/ + content: string; +}