feat!: 将项目有Strapi迁移至Directus #43
@ -8,3 +8,4 @@ export * from './useQuestionList';
|
|||||||
export * from './useDocumentList';
|
export * from './useDocumentList';
|
||||||
export * from './useContactInfo';
|
export * from './useContactInfo';
|
||||||
export * from './useCompanyProfile';
|
export * from './useCompanyProfile';
|
||||||
|
export * from './useHomepage';
|
||||||
|
|||||||
54
app/composables/directus/useHomepage.ts
Normal file
54
app/composables/directus/useHomepage.ts
Normal file
@ -0,0 +1,54 @@
|
|||||||
|
import { readSingleton } from '@directus/sdk';
|
||||||
|
|
||||||
|
export const useHomepage = () => {
|
||||||
|
const { $directus } = useNuxtApp();
|
||||||
|
const { getDirectusLocale } = useLocalizations();
|
||||||
|
const locale = getDirectusLocale();
|
||||||
|
|
||||||
|
return useAsyncData(`homepage-${locale}`, async () => {
|
||||||
|
return await $directus.request(
|
||||||
|
readSingleton('homepage', {
|
||||||
|
fields: [
|
||||||
|
'id',
|
||||||
|
{
|
||||||
|
carousel: ['id', 'directus_files_id'],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
recommend_products: [
|
||||||
|
'id',
|
||||||
|
{
|
||||||
|
translations: ['id', 'name', 'summary'],
|
||||||
|
},
|
||||||
|
'cover',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
{
|
||||||
|
recommend_solutions: [
|
||||||
|
'id',
|
||||||
|
{
|
||||||
|
translations: ['id', 'title', 'summary'],
|
||||||
|
},
|
||||||
|
'cover',
|
||||||
|
],
|
||||||
|
},
|
||||||
|
],
|
||||||
|
deep: {
|
||||||
|
recommend_products: {
|
||||||
|
translations: {
|
||||||
|
_filter: {
|
||||||
|
languages_code: { _eq: locale },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
recommend_solutions: {
|
||||||
|
translations: {
|
||||||
|
_filter: {
|
||||||
|
languages_code: { _eq: locale },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
51
app/models/mappers/homepageMapper.ts
Normal file
51
app/models/mappers/homepageMapper.ts
Normal file
@ -0,0 +1,51 @@
|
|||||||
|
/**
|
||||||
|
* 将 Directus 返回的 Homepage 数据转换为 HomepageView 视图模型
|
||||||
|
*
|
||||||
|
* @param raw: 原始的 Homepage 数据
|
||||||
|
* @returns 转换后的 HomepageView 对象
|
||||||
|
*
|
||||||
|
* @example
|
||||||
|
* const view = toHomepageView(rawHomepage);
|
||||||
|
*/
|
||||||
|
export function toHomepageView(raw: Homepage): HomepageView {
|
||||||
|
const carousel = (raw.carousel ?? [])
|
||||||
|
.filter(isObject<HomepageFile>)
|
||||||
|
.map((item) => item.directus_files_id)
|
||||||
|
.filter((item) => typeof item === 'string');
|
||||||
|
|
||||||
|
const products = (raw.recommend_products ?? [])
|
||||||
|
.filter(isObject<Product>)
|
||||||
|
.map((item) => {
|
||||||
|
const cover = isObject<DirectusFile>(item.cover)
|
||||||
|
? item.cover.id
|
||||||
|
: item.cover;
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
name: item.translations?.[0].name,
|
||||||
|
summary: item.translations?.[0].summary,
|
||||||
|
cover: cover,
|
||||||
|
} satisfies HomepageProductView;
|
||||||
|
});
|
||||||
|
|
||||||
|
const solutions = (raw.recommend_solutions ?? [])
|
||||||
|
.filter(isObject<Solution>)
|
||||||
|
.map((item) => {
|
||||||
|
const cover = isObject<DirectusFile>(item.cover)
|
||||||
|
? item.cover.id
|
||||||
|
: item.cover;
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: item.id,
|
||||||
|
title: item.translations?.[0].title,
|
||||||
|
summary: item.translations?.[0].summary,
|
||||||
|
cover: cover,
|
||||||
|
} satisfies HomepageSolutionView;
|
||||||
|
});
|
||||||
|
|
||||||
|
return {
|
||||||
|
id: raw.id,
|
||||||
|
carousel: carousel ?? [],
|
||||||
|
recommendProducts: products ?? [],
|
||||||
|
recommendSolutions: solutions ?? [],
|
||||||
|
};
|
||||||
|
}
|
||||||
50
app/models/views/HomepageView.ts
Normal file
50
app/models/views/HomepageView.ts
Normal file
@ -0,0 +1,50 @@
|
|||||||
|
/**
|
||||||
|
* 主页推荐产品视图模型
|
||||||
|
*/
|
||||||
|
export interface HomepageProductView {
|
||||||
|
/** 唯一标识符 **/
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
/** 产品名称 **/
|
||||||
|
name: string;
|
||||||
|
|
||||||
|
/** 产品简介 **/
|
||||||
|
summary: string;
|
||||||
|
|
||||||
|
/** 产品封面 **/
|
||||||
|
cover: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主页推荐解决方案视图模型
|
||||||
|
*/
|
||||||
|
export interface HomepageSolutionView {
|
||||||
|
/** 唯一标识符 **/
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
/** 解决方案标题 **/
|
||||||
|
title: string;
|
||||||
|
|
||||||
|
/** 解决方案摘要 **/
|
||||||
|
summary: string;
|
||||||
|
|
||||||
|
/** 解决方案封面 **/
|
||||||
|
cover: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 主页视图模型
|
||||||
|
*/
|
||||||
|
export interface HomepageView {
|
||||||
|
/** 唯一标识符 **/
|
||||||
|
id: number;
|
||||||
|
|
||||||
|
/** 首页图片 **/
|
||||||
|
carousel: string[];
|
||||||
|
|
||||||
|
/** 首页推荐产品 **/
|
||||||
|
recommendProducts: HomepageProductView[];
|
||||||
|
|
||||||
|
/** 首页推荐解决方案 **/
|
||||||
|
recommendSolutions: HomepageSolutionView[];
|
||||||
|
}
|
||||||
@ -12,14 +12,11 @@
|
|||||||
<div class="carousel-item">
|
<div class="carousel-item">
|
||||||
<el-image
|
<el-image
|
||||||
class="carousel-image"
|
class="carousel-image"
|
||||||
:src="useStrapiMedia(item.url || '')"
|
:src="getImageUrl(item)"
|
||||||
:alt="item.alternativeText || `Carousel Image ${index + 1}`"
|
:alt="`Carousel Image ${index + 1}`"
|
||||||
fit="contain"
|
fit="contain"
|
||||||
lazy
|
lazy
|
||||||
/>
|
/>
|
||||||
<p v-if="item.caption" class="carousel-image-caption">
|
|
||||||
{{ item.caption }}
|
|
||||||
</p>
|
|
||||||
</div>
|
</div>
|
||||||
</el-carousel-item>
|
</el-carousel-item>
|
||||||
</el-carousel>
|
</el-carousel>
|
||||||
@ -42,24 +39,24 @@
|
|||||||
:autoplay="false"
|
:autoplay="false"
|
||||||
>
|
>
|
||||||
<el-carousel-item
|
<el-carousel-item
|
||||||
v-for="n in Math.floor(recommend_productions.length / 3) + 1"
|
v-for="n in Math.floor(recommend_products.length / 3) + 1"
|
||||||
:key="n"
|
:key="n"
|
||||||
class="recommend-list"
|
class="recommend-list"
|
||||||
>
|
>
|
||||||
<div class="recommend-card-group">
|
<div class="recommend-card-group">
|
||||||
<el-card
|
<el-card
|
||||||
v-for="(item, index) in recommend_productions.slice(
|
v-for="(item, index) in recommend_products.slice(
|
||||||
(n - 1) * 3,
|
(n - 1) * 3,
|
||||||
n * 3
|
n * 3
|
||||||
)"
|
)"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="recommend-card"
|
class="recommend-card"
|
||||||
@click="handleProductionCardClick(item.documentId || '')"
|
@click="handleProductionCardClick(item.id.toString() || '')"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-image
|
<el-image
|
||||||
:src="useStrapiMedia(item.cover?.url || '')"
|
:src="getImageUrl(item.cover)"
|
||||||
:alt="item.cover?.alternativeText || item.title"
|
:alt="item.name"
|
||||||
fit="cover"
|
fit="cover"
|
||||||
lazy
|
lazy
|
||||||
/>
|
/>
|
||||||
@ -67,7 +64,7 @@
|
|||||||
<div class="recommend-card-body">
|
<div class="recommend-card-body">
|
||||||
<!-- Title -->
|
<!-- Title -->
|
||||||
<div class="text-center">
|
<div class="text-center">
|
||||||
<span class="recommend-card-title">{{ item.title }}</span>
|
<span class="recommend-card-title">{{ item.name }}</span>
|
||||||
</div>
|
</div>
|
||||||
<!-- Description -->
|
<!-- Description -->
|
||||||
<div class="recommend-card-description text-left opacity-25">
|
<div class="recommend-card-description text-left opacity-25">
|
||||||
@ -107,12 +104,12 @@
|
|||||||
)"
|
)"
|
||||||
:key="index"
|
:key="index"
|
||||||
class="recommend-card"
|
class="recommend-card"
|
||||||
@click="handleSolutionCardClick(item.documentId || '')"
|
@click="handleSolutionCardClick(item.id.toString() || '')"
|
||||||
>
|
>
|
||||||
<template #header>
|
<template #header>
|
||||||
<el-image
|
<el-image
|
||||||
:src="useStrapiMedia(item.cover?.url || '')"
|
:src="getImageUrl(item.cover)"
|
||||||
:alt="item.cover?.alternativeText || item.title"
|
:alt="item.title"
|
||||||
fit="cover"
|
fit="cover"
|
||||||
lazy
|
lazy
|
||||||
/>
|
/>
|
||||||
@ -140,43 +137,27 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
const { findOne } = useStrapi();
|
const { getImageUrl } = useDirectusImage();
|
||||||
const { getStrapiLocale } = useLocalizations();
|
|
||||||
const strapiLocale = getStrapiLocale();
|
|
||||||
|
|
||||||
const { data, pending, error } = useAsyncData('homepage', () =>
|
const { data, pending, error } = await useHomepage();
|
||||||
findOne<StrapiHomepage>('homepage', undefined, {
|
|
||||||
populate: {
|
|
||||||
carousel: {
|
|
||||||
populate: '*',
|
|
||||||
},
|
|
||||||
recommend_productions: {
|
|
||||||
populate: {
|
|
||||||
cover: {
|
|
||||||
populate: '*',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
recommend_solutions: {
|
|
||||||
populate: {
|
|
||||||
cover: {
|
|
||||||
populate: '*',
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
locale: strapiLocale,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
|
|
||||||
const carousel = computed(() => data.value?.data.carousel || []);
|
const homepageData = computed(() => {
|
||||||
const recommend_productions = computed(
|
return toHomepageView(data.value);
|
||||||
() => data.value?.data.recommend_productions || []
|
});
|
||||||
|
|
||||||
|
const carousel = computed(() => homepageData.value?.carousel);
|
||||||
|
|
||||||
|
const recommend_products = computed(
|
||||||
|
() => homepageData.value?.recommendProducts
|
||||||
);
|
);
|
||||||
const recommend_solutions = computed(
|
const recommend_solutions = computed(
|
||||||
() => data.value?.data.recommend_solutions || []
|
() => homepageData.value?.recommendSolutions
|
||||||
);
|
);
|
||||||
|
|
||||||
|
watch(pending, () => {
|
||||||
|
console.log(data.value);
|
||||||
|
});
|
||||||
|
|
||||||
watch(error, (value) => {
|
watch(error, (value) => {
|
||||||
if (value) {
|
if (value) {
|
||||||
console.error('数据获取失败: ', value);
|
console.error('数据获取失败: ', value);
|
||||||
|
|||||||
Reference in New Issue
Block a user