Compare commits

...

3 Commits

Author SHA1 Message Date
227b537a0f feat: 产品页的组合式API
All checks were successful
deploy to server / build-and-deploy (push) Successful in 5m11s
- 为product和product-list添加组合式API
2025-10-16 16:02:20 +08:00
6c76d81a40 feat: 为directus做I18n适配
- 添加getDirectusLocale方法用于获取Directus本地化字段
2025-10-16 15:14:46 +08:00
202657e634 chore: 调整目录结构
- 将Directus相关的组合式API移入composables/direcuts
2025-10-16 14:56:46 +08:00
9 changed files with 240 additions and 179 deletions

View File

@ -0,0 +1,4 @@
export * from './useDirectusImage';
export * from './useDirectusFiles';
export * from './useProductList';
export * from './useProduct';

View File

@ -0,0 +1,111 @@
import { readItem } from '@directus/sdk';
export const useProduct = (id: string) => {
const { $directus } = useNuxtApp();
const { getDirectusLocale } = useLocalizations();
const locale = getDirectusLocale();
return useAsyncData(`product-${id}-${locale}`, async () => {
return await $directus.request(
readItem('products', id, {
fields: [
'id',
{ translations: ['id', 'name', 'summary', 'description'] },
{
images: [
'id',
{
product_images_id: [
'id',
'image',
{ translations: ['id', 'caption'] },
],
},
],
},
{
specs: [
'id',
{
translations: ['*'],
},
{
specs: [
'id',
{
translations: ['id', 'key'],
},
'value',
],
},
],
},
{
faqs: [
'id',
{
questions_id: [
'id',
{
translations: ['id', 'title', 'content'],
},
],
},
],
},
{
documents: [
'id',
{
product_documents_id: [
'id',
{
file: ['id', 'filesize', 'filename_download'],
},
{
translations: ['id', 'title'],
},
],
},
],
},
],
deep: {
translations: {
_filter: {
languages_code: { _eq: locale },
},
},
images: {
product_images_id: {
translations: {
_filter: {
languages_code: { _eq: locale },
},
},
},
},
faqs: {
questions_id: {
translations: {
_filter: {
languages_code: { _eq: locale },
},
},
},
},
documents: {
documents_id: {
translations: {
_filter: {
languages_code: { _eq: locale },
},
},
},
},
},
})
);
});
};

View File

@ -0,0 +1,42 @@
import { readItems } from '@directus/sdk';
export const useProductList = () => {
const { $directus } = useNuxtApp();
const { getDirectusLocale } = useLocalizations();
const locale = getDirectusLocale();
return useAsyncData(`product-list-${locale}`, async () => {
return await $directus.request(
readItems('products', {
fields: [
'id',
{ translations: ['id', 'name', 'summary'] },
'cover',
{
product_type: [
'id',
{
translations: ['id', 'name'],
},
],
},
],
deep: {
translations: {
_filter: {
languages_code: { _eq: locale },
},
},
product_type: {
translations: {
_filter: {
languages_code: { _eq: locale },
},
},
},
},
})
);
});
};

3
app/composables/index.ts Normal file
View File

@ -0,0 +1,3 @@
export * from './directus';
export * from './useLocalizations';
export * from './useMeilisearch';

View File

@ -3,38 +3,89 @@ import type { Language as ElementLanguage } from 'element-plus/es/locale';
import zhCn from 'element-plus/es/locale/lang/zh-cn';
import en from 'element-plus/es/locale/lang/en';
// Strapi本地化映射
export const strapiLocales: Record<string, StrapiLocale> = {
zh: 'zh-CN',
en: 'en',
};
/**
* 应用语言映射结构
* 用于统一 Strapi / Directus / Element Plus 的多语言配置
*/
export interface LocaleMapping {
/** 用于StrapiLocale **/
strapi: StrapiLocale;
/** 用于Directus translations.languages_code **/
directus: string;
/** Element Plus语言对象 **/
element: ElementLanguage;
}
// Element Plus本地化映射
export const elementPlusLocales: Record<string, ElementLanguage> = {
zh: zhCn,
en: en,
};
/**
* 应用支持的语言映射表。
*
* 每个键(如 "zh"、"en")对应一套统一的本地化配置,
* 方便在 Strapi / Directus / Element Plus 三方系统间保持一致。
*/
export const localeMap = {
zh: {
strapi: 'zh-CN',
directus: 'zh-CN',
element: zhCn,
},
en: {
strapi: 'en',
directus: 'en-US',
element: en,
},
} satisfies Record<string, LocaleMapping>;
/** 应用支持的语言键类型 **/
export type AppLocale = keyof typeof localeMap;
/** 默认语言, 当找不到匹配语言时回退到默认语言 **/
const defaultLocale: AppLocale = 'zh';
/**
* 提供 Strapi、 Directus 与 Element Plus的国际化映射工具
*
* ---
* @example
* ``` ts
* const { locale, getStrapiLocale, getElementPlusLocale } = useLocalizations()
*
* const strapiLang = getStrapiLocale() // 当前 Strapi 语言码
* const elLocale = getElementPlusLocale('en') // 获取 Element Plus 英文对象
* const all = getLocaleMapping('zh') // 获取完整映射结构
* ```
* ---
*
* @returns 返回当前语言及各系统的本地化获取方法
*/
export const useLocalizations = () => {
const { locale } = useI18n();
const { locale } = useI18n<{ locale: Ref<AppLocale> }>();
// 获取Strapi本地化代码
const getStrapiLocale = (nuxtLocale?: string): StrapiLocale => {
const currentLocale = nuxtLocale || locale.value;
return strapiLocales[currentLocale] || 'zh-Hans';
};
// 获取Element Plus本地化
const getElementPlusLocale = (nuxtLocale?: string) => {
const currentLocale = nuxtLocale || locale.value;
const elementPlusLocale =
elementPlusLocales[currentLocale] || elementPlusLocales['zh'];
return elementPlusLocale;
/**
* 获取对应语言的完整映射结构
*
* @param nuxtLocale - 可选的语言码参数,若提供则使用该语言码,否则使用当前应用语言
* @returns 返回当前语言的完整映射对象
*/
const getMapping = (nuxtLocale?: AppLocale): LocaleMapping => {
const current = nuxtLocale || locale.value;
return localeMap[current] || localeMap[defaultLocale];
};
return {
/** 当前Nuxt I18n语言(只读) **/
locale: readonly(locale),
getStrapiLocale,
getElementPlusLocale,
/** 获取Strapi的本地化代码 **/
getStrapiLocale: (l?: AppLocale) => getMapping(l).strapi,
/** 获取Directus的本地化代码 **/
getDirectusLocale: (l?: AppLocale) => getMapping(l).directus,
/** 获取Element Plus语言对象 **/
getElementPlusLocale: (l?: AppLocale) => getMapping(l).element,
/**
* 获取完整的语言映射结构(Strapi / Directus / Element Plus)
*
* @param l: 指定语言默认为当前locale
* @returns 语言映射对象
*/
getLocaleMapping: getMapping,
};
};

View File

@ -106,121 +106,14 @@
</template>
<script setup lang="ts">
import { readItem } from '@directus/sdk';
const route = useRoute();
const { getStrapiLocale } = useLocalizations();
const strapiLocale = getStrapiLocale();
const { $directus } = useNuxtApp();
const { getImageUrl } = useDirectusImage();
// 获取路由参数slug 或 id
const documentId = computed(() => route.params.slug as string);
// 获取路由参数
const id = computed(() => route.params.slug as string);
const { data, pending, error } = await useAsyncData(
() => `production-${documentId.value}`,
() =>
$directus.request(
readItem('products', documentId.value, {
fields: [
'id',
{ translations: ['id', 'name', 'summary', 'description'] },
{
images: [
'id',
{
product_images_id: [
'id',
'image',
{ translations: ['id', 'caption'] },
],
},
],
},
{
specs: [
'id',
{
translations: ['*'],
},
{
specs: [
'id',
{
translations: ['id', 'key'],
},
'value',
],
},
],
},
{
faqs: [
'id',
{
questions_id: [
'id',
{
translations: ['id', 'title', 'content'],
},
],
},
],
},
{
documents: [
'id',
{
product_documents_id: [
'id',
{
file: ['id', 'filesize', 'filename_download'],
},
{
translations: ['id', 'title'],
},
],
},
],
},
],
deep: {
translations: {
_filter: {
languages_code: { _eq: strapiLocale },
},
},
images: {
product_images_id: {
translations: {
_filter: {
languages_code: { _eq: strapiLocale },
},
},
},
},
faqs: {
questions_id: {
translations: {
_filter: {
languages_code: { _eq: strapiLocale },
},
},
},
},
documents: {
documents_id: {
translations: {
_filter: {
languages_code: { _eq: strapiLocale },
},
},
},
},
},
})
)
);
const { data, pending, error } = await useProduct(id.value);
console.log('Raw Data: ', data.value);

View File

@ -45,55 +45,12 @@
</template>
<script setup lang="ts">
import { readItems } from '@directus/sdk';
const { getStrapiLocale } = useLocalizations();
const strapiLocale = getStrapiLocale();
const { $directus } = useNuxtApp();
const { getImageUrl } = useDirectusImage();
const { data, pending, error } = useAsyncData(
'products',
() =>
$directus.request(
readItems('products', {
fields: [
'id',
{ translations: ['id', 'name', 'summary'] },
'cover',
{
product_type: [
'id',
{
translations: ['id', 'name'],
},
],
},
],
deep: {
translations: {
_filter: {
languages_code: { _eq: strapiLocale },
},
},
product_type: {
translations: {
_filter: {
languages_code: { _eq: strapiLocale },
},
},
},
},
})
),
{
lazy: true,
}
);
const { data, pending, error } = useProductList();
const activeNames = ref<string[]>([]);
// const productions = computed(() => data.value?.data ?? []);
const productionsRaw = computed(() => data.value ?? []);
const productions = computed(() =>
productionsRaw.value.map((item) => toProductListView(item))