Compare commits
3 Commits
cb861bc955
...
227b537a0f
| Author | SHA1 | Date | |
|---|---|---|---|
| 227b537a0f | |||
| 6c76d81a40 | |||
| 202657e634 |
4
app/composables/directus/index.ts
Normal file
4
app/composables/directus/index.ts
Normal file
@ -0,0 +1,4 @@
|
|||||||
|
export * from './useDirectusImage';
|
||||||
|
export * from './useDirectusFiles';
|
||||||
|
export * from './useProductList';
|
||||||
|
export * from './useProduct';
|
||||||
111
app/composables/directus/useProduct.ts
Normal file
111
app/composables/directus/useProduct.ts
Normal 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 },
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
},
|
||||||
|
})
|
||||||
|
);
|
||||||
|
});
|
||||||
|
};
|
||||||
42
app/composables/directus/useProductList.ts
Normal file
42
app/composables/directus/useProductList.ts
Normal 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
3
app/composables/index.ts
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
export * from './directus';
|
||||||
|
export * from './useLocalizations';
|
||||||
|
export * from './useMeilisearch';
|
||||||
@ -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 zhCn from 'element-plus/es/locale/lang/zh-cn';
|
||||||
import en from 'element-plus/es/locale/lang/en';
|
import en from 'element-plus/es/locale/lang/en';
|
||||||
|
|
||||||
// Strapi本地化映射
|
/**
|
||||||
export const strapiLocales: Record<string, StrapiLocale> = {
|
* 应用语言映射结构
|
||||||
zh: 'zh-CN',
|
* 用于统一 Strapi / Directus / Element Plus 的多语言配置
|
||||||
en: 'en',
|
*/
|
||||||
};
|
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 = () => {
|
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';
|
* @param nuxtLocale - 可选的语言码参数,若提供则使用该语言码,否则使用当前应用语言
|
||||||
};
|
* @returns 返回当前语言的完整映射对象
|
||||||
|
*/
|
||||||
// 获取Element Plus本地化
|
const getMapping = (nuxtLocale?: AppLocale): LocaleMapping => {
|
||||||
const getElementPlusLocale = (nuxtLocale?: string) => {
|
const current = nuxtLocale || locale.value;
|
||||||
const currentLocale = nuxtLocale || locale.value;
|
return localeMap[current] || localeMap[defaultLocale];
|
||||||
const elementPlusLocale =
|
|
||||||
elementPlusLocales[currentLocale] || elementPlusLocales['zh'];
|
|
||||||
return elementPlusLocale;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
return {
|
return {
|
||||||
|
/** 当前Nuxt I18n语言(只读) **/
|
||||||
locale: readonly(locale),
|
locale: readonly(locale),
|
||||||
getStrapiLocale,
|
/** 获取Strapi的本地化代码 **/
|
||||||
getElementPlusLocale,
|
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,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|||||||
@ -106,121 +106,14 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { readItem } from '@directus/sdk';
|
|
||||||
const route = useRoute();
|
const route = useRoute();
|
||||||
const { getStrapiLocale } = useLocalizations();
|
|
||||||
const strapiLocale = getStrapiLocale();
|
|
||||||
|
|
||||||
const { $directus } = useNuxtApp();
|
|
||||||
const { getImageUrl } = useDirectusImage();
|
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(
|
const { data, pending, error } = await useProduct(id.value);
|
||||||
() => `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 },
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
},
|
|
||||||
})
|
|
||||||
)
|
|
||||||
);
|
|
||||||
|
|
||||||
console.log('Raw Data: ', data.value);
|
console.log('Raw Data: ', data.value);
|
||||||
|
|
||||||
|
|||||||
@ -45,55 +45,12 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { readItems } from '@directus/sdk';
|
|
||||||
const { getStrapiLocale } = useLocalizations();
|
|
||||||
const strapiLocale = getStrapiLocale();
|
|
||||||
|
|
||||||
const { $directus } = useNuxtApp();
|
|
||||||
const { getImageUrl } = useDirectusImage();
|
const { getImageUrl } = useDirectusImage();
|
||||||
|
|
||||||
const { data, pending, error } = useAsyncData(
|
const { data, pending, error } = useProductList();
|
||||||
'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 activeNames = ref<string[]>([]);
|
const activeNames = ref<string[]>([]);
|
||||||
|
|
||||||
// const productions = computed(() => data.value?.data ?? []);
|
|
||||||
const productionsRaw = computed(() => data.value ?? []);
|
const productionsRaw = computed(() => data.value ?? []);
|
||||||
const productions = computed(() =>
|
const productions = computed(() =>
|
||||||
productionsRaw.value.map((item) => toProductListView(item))
|
productionsRaw.value.map((item) => toProductListView(item))
|
||||||
|
|||||||
Reference in New Issue
Block a user