官网开发中 0803
This commit is contained in:
@@ -0,0 +1,127 @@
|
||||
<script setup lang="ts">
|
||||
import type { AboutConfig, AboutFeature } from '~/types/site';
|
||||
import { parseJson } from '~/utils/parseJson';
|
||||
|
||||
const { t, locale } = useI18n();
|
||||
const localePath = useLocalePath();
|
||||
const { getPageConfig } = useSiteApi();
|
||||
|
||||
// SEO:关于我们
|
||||
await usePageSeo('about', undefined, `${t('app.name')} - ${t('nav.about')}`);
|
||||
|
||||
const config = ref<AboutConfig | null>(null);
|
||||
const loading = ref(true);
|
||||
|
||||
onMounted(async () => {
|
||||
try {
|
||||
config.value = await getPageConfig<AboutConfig>('about');
|
||||
} catch {
|
||||
config.value = null;
|
||||
} finally {
|
||||
loading.value = false;
|
||||
}
|
||||
});
|
||||
|
||||
// ===== 按语言取值 =====
|
||||
const heroTitle = computed(() => (locale.value === 'en' ? config.value?.hero_title_en : config.value?.hero_title_zh) || (locale.value === 'en' ? 'About Us' : '关于我们'));
|
||||
const heroSubtitle = computed(() => (locale.value === 'en' ? config.value?.hero_subtitle_en : config.value?.hero_subtitle_zh) || '');
|
||||
const s1Title = computed(() => (locale.value === 'en' ? config.value?.s1_title_en : config.value?.s1_title_zh) || '');
|
||||
const s1Content = computed(() => (locale.value === 'en' ? config.value?.s1_content_en : config.value?.s1_content_zh) || '');
|
||||
const s1Cta = computed(() => (locale.value === 'en' ? config.value?.s1_cta_en : config.value?.s1_cta_zh) || '');
|
||||
const s2Title = computed(() => (locale.value === 'en' ? config.value?.s2_title_en : config.value?.s2_title_zh) || '');
|
||||
const s3Title = computed(() => (locale.value === 'en' ? config.value?.s3_title_en : config.value?.s3_title_zh) || '');
|
||||
const s3Cta = computed(() => (locale.value === 'en' ? config.value?.s3_cta_en : config.value?.s3_cta_zh) || '');
|
||||
|
||||
// s2_features:JSON 字符串或数组
|
||||
const features = computed<AboutFeature[]>(() =>
|
||||
parseJson<AboutFeature[]>(config.value?.s2_features, [])
|
||||
);
|
||||
|
||||
function featureTitle(f: AboutFeature): string {
|
||||
return locale.value === 'en' ? f.title_en : f.title_zh;
|
||||
}
|
||||
function featureDesc(f: AboutFeature): string {
|
||||
return locale.value === 'en' ? f.desc_en : f.desc_zh;
|
||||
}
|
||||
|
||||
// 是否需要展示各区块
|
||||
const hasS1 = computed(() => Boolean(s1Title.value || s1Content.value || config.value?.s1_image_url));
|
||||
const hasS2 = computed(() => Boolean(s2Title.value && features.value.length));
|
||||
const hasS3 = computed(() => Boolean(s3Title.value || s3Cta.value));
|
||||
</script>
|
||||
|
||||
<template>
|
||||
<div>
|
||||
<PageHero
|
||||
:title="heroTitle"
|
||||
:subtitle="heroSubtitle || undefined"
|
||||
:bg-url="config?.hero_bg_url || undefined"
|
||||
/>
|
||||
|
||||
<div v-if="loading" class="py-80px text-center text-sm text-gray-400">
|
||||
{{ t('common.loading') }}
|
||||
</div>
|
||||
|
||||
<div v-else>
|
||||
<!-- S1:图文介绍 -->
|
||||
<section v-if="hasS1" class="section-container py-64px md:py-96px">
|
||||
<div class="grid md:grid-cols-2 gap-40px md:gap-64px items-center">
|
||||
<div class="flex flex-col gap-20px order-2 md:order-1">
|
||||
<h2 v-if="s1Title" class="text-2xl md:text-3xl font-bold text-gray-900 dark:text-white">{{ s1Title }}</h2>
|
||||
<!-- 富文本(dashboard 端为 HTML 内容) -->
|
||||
<div v-if="s1Content" class="text-base md:text-lg text-gray-600 dark:text-gray-300 leading-relaxed" v-html="s1Content" />
|
||||
<div v-if="s1Cta">
|
||||
<NuxtLink
|
||||
:to="localePath(config?.s1_cta_url || '/contact')"
|
||||
class="inline-block px-28px py-12px rounded-8px bg-primary text-white text-sm font-medium hover:opacity-90 transition-opacity"
|
||||
>
|
||||
{{ s1Cta }}
|
||||
</NuxtLink>
|
||||
</div>
|
||||
</div>
|
||||
<div v-if="config?.s1_image_url" class="order-1 md:order-2">
|
||||
<img
|
||||
:src="config.s1_image_url"
|
||||
:alt="s1Title"
|
||||
class="w-full rounded-16px shadow-lg object-cover aspect-[4/3]"
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- S2:核心优势 -->
|
||||
<section v-if="hasS2" class="bg-gray-50 dark:bg-dark-900 border-y border-gray-100 dark:border-dark-700">
|
||||
<div class="section-container py-64px md:py-96px">
|
||||
<h2 class="text-center text-2xl md:text-3xl font-bold text-gray-900 dark:text-white mb-48px">{{ s2Title }}</h2>
|
||||
<div class="grid gap-24px sm:grid-cols-2 lg:grid-cols-3">
|
||||
<div
|
||||
v-for="(f, idx) in features"
|
||||
:key="idx"
|
||||
class="p-24px rounded-16px bg-white dark:bg-dark-800 border border-gray-100 dark:border-dark-700 hover:shadow-lg transition-shadow"
|
||||
>
|
||||
<h3 class="font-semibold text-gray-900 dark:text-white mb-8px">{{ featureTitle(f) }}</h3>
|
||||
<p class="text-sm text-gray-500 dark:text-gray-400 leading-relaxed">{{ featureDesc(f) }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</section>
|
||||
|
||||
<!-- S3:CTA -->
|
||||
<section v-if="hasS3" class="section-container py-64px md:py-96px text-center">
|
||||
<h2 class="text-2xl md:text-3xl font-bold text-gray-900 dark:text-white mb-32px">{{ s3Title }}</h2>
|
||||
<NuxtLink
|
||||
v-if="s3Cta"
|
||||
:to="localePath(config?.s3_cta_url || '/contact')"
|
||||
class="inline-block px-32px py-12px rounded-8px bg-primary text-white text-sm font-medium hover:opacity-90 transition-opacity"
|
||||
>
|
||||
{{ s3Cta }}
|
||||
</NuxtLink>
|
||||
</section>
|
||||
|
||||
<!-- 空配置提示 -->
|
||||
<div v-if="!hasS1 && !hasS2 && !hasS3" class="section-container py-80px text-center">
|
||||
<p class="text-gray-400">{{ t('common.empty') }}</p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
Reference in New Issue
Block a user