官网开发 0805

This commit is contained in:
eafonyang
2026-08-05 18:36:01 +08:00
parent 816281bf2d
commit c9a1d9c576
23 changed files with 1367 additions and 231 deletions
@@ -0,0 +1,110 @@
<script setup lang="ts">
import type { SectionBlock } from '~/types/site';
import { resolveAssetUrl } from '~/utils/resolveAssetUrl';
const props = defineProps<{ section: SectionBlock }>();
const { overline, title, subtitle, config, isEn, isDark, overlineStyle, titleStyle, bodyStyle } = useSectionContent(props.section);
type MosaicPattern = 'big_3' | 'half_2' | 'big_4' | 'big_2';
// 拼贴版式(config.mosaic_pattern):big_3 一大三小(默认)/ half_2 左右等分 / big_4 一大四小 / big_2 一大二小上下
const pattern = computed<MosaicPattern>(() => {
const p = config.value.mosaic_pattern;
return p === 'half_2' || p === 'big_4' || p === 'big_2' ? p : 'big_3';
});
// 各版式槽位数量
const slotCount: Record<MosaicPattern, number> = { big_3: 4, half_2: 2, big_4: 5, big_2: 3 };
// 槽位素材(config.mosaic_items):media_zh/_en 以语言后缀结尾,不命中数据层 resolveAssetUrls,需手动解析相对路径
const items = computed(() => {
const raw = (config.value.mosaic_items ?? []) as Array<{ type?: string; media_zh?: string; media_en?: string }>;
return Array.from({ length: slotCount[pattern.value] }, (_, i) => {
const it = raw[i] ?? {};
const src = (isEn.value ? it.media_en || it.media_zh : it.media_zh || it.media_en) || '';
return { type: it.type === 'video' ? 'video' : 'image', url: resolveAssetUrl(src) };
});
});
// 网格高度(config.min_height,与主视觉分屏共用字段):仅桌面端生效;
// 未设置 = 默认 16/9 宽高比;>0 = 固定高度 px(格子媒体 object-cover 裁剪填充),移动端保持单列 4/3 堆叠
const gridHeight = computed(() => {
const v = (props.section.config as Record<string, unknown> | null)?.min_height;
return typeof v === 'number' && v > 0 ? `${v}px` : '';
});
// 容器网格 class(完整字面量供 UnoCSS 提取;容器定高后行轨道均分,格子内媒体 object-cover 填充)
const gridClass = computed(() => {
const base = (
{
big_3: 'md:grid-cols-4 md:grid-rows-2',
half_2: 'md:grid-cols-2',
big_4: 'md:grid-cols-4 md:grid-rows-2',
big_2: 'md:grid-cols-2 md:grid-rows-2'
} as Record<MosaicPattern, string>
)[pattern.value];
// 自定义高度时改用固定高(md:h-[var(--mosaic-h)]),否则保持默认宽高比
const sizing = gridHeight.value
? 'md:h-[var(--mosaic-h)]'
: (
{
big_3: 'md:aspect-16/9',
half_2: '',
big_4: 'md:aspect-16/9',
big_2: 'md:aspect-16/9'
} as Record<MosaicPattern, string>
)[pattern.value];
return `${base} ${sizing}`;
});
// 格子跨行跨列 class(按槽位下标;移动端统一单列 4/3 堆叠)
function cellClass(idx: number) {
switch (pattern.value) {
case 'big_3':
if (idx === 0) return 'aspect-4/3 md:aspect-auto md:col-span-2 md:row-span-2';
if (idx === 3) return 'aspect-4/3 md:aspect-auto md:col-span-2';
return 'aspect-4/3 md:aspect-auto';
case 'half_2':
return 'aspect-4/3';
case 'big_4':
if (idx === 0) return 'aspect-4/3 md:aspect-auto md:col-span-2 md:row-span-2';
return 'aspect-4/3 md:aspect-auto';
case 'big_2':
if (idx === 0) return 'aspect-4/3 md:aspect-auto md:row-span-2';
return 'aspect-4/3 md:aspect-auto';
default:
return 'aspect-4/3';
}
}
</script>
<template>
<div class="section-container py-[var(--sec-py,96px)]">
<!-- 标题区 -->
<div v-if="overline || title || subtitle" class="text-center max-w-640px mx-auto mb-56px">
<p v-if="overline" class="text-14px font-semibold tracking-widest uppercase" :class="isDark ? 'text-primary-300' : 'text-primary'" :style="overlineStyle">
{{ overline }}
</p>
<h2 class="mt-12px font-bold" :class="isDark ? 'text-white' : 'text-gray-900 dark:text-white'" :style="titleStyle">
{{ title }}
</h2>
<p v-if="subtitle" class="mt-16px text-base" :class="isDark ? 'text-gray-400' : 'text-gray-500 dark:text-gray-400'" :style="bodyStyle">
{{ subtitle }}
</p>
</div>
<!-- 拼贴网格自定义高度时通过 --mosaic-h 下发仅桌面端 -->
<div class="grid gap-16px" :class="gridClass" :style="gridHeight ? { '--mosaic-h': gridHeight } : {}">
<div
v-for="(item, idx) in items"
:key="idx"
class="overflow-hidden rounded-16px bg-gray-100 dark:bg-gray-800"
:class="cellClass(idx)"
>
<video v-if="item.type === 'video' && item.url" :src="item.url" class="w-full h-full object-cover" autoplay muted loop playsinline preload="metadata" />
<img v-else-if="item.url" :src="item.url" :alt="title" class="w-full h-full object-cover" loading="lazy" />
</div>
</div>
</div>
</template>