2026-07-31 19:02:00 +08:00
|
|
|
|
<script setup lang="ts">
|
|
|
|
|
|
import type { SectionBlock } from '~/types/site';
|
2026-08-04 19:18:35 +08:00
|
|
|
|
import { resolveAssetUrl } from '~/utils/resolveAssetUrl';
|
2026-07-31 19:02:00 +08:00
|
|
|
|
|
|
|
|
|
|
const props = defineProps<{ section: SectionBlock }>();
|
|
|
|
|
|
|
2026-08-03 19:09:03 +08:00
|
|
|
|
const { overline, title, subtitle, content, mediaUrl, isDark, overlineStyle, titleStyle, bodyStyle } = useSectionContent(props.section);
|
2026-08-04 19:18:35 +08:00
|
|
|
|
|
|
|
|
|
|
// 分栏媒体视频(video_url 有值时优先渲染视频,支持 /uploads/... 相对路径)
|
|
|
|
|
|
const videoUrl = computed(() => resolveAssetUrl(props.section.video_url));
|
|
|
|
|
|
|
|
|
|
|
|
// 媒体位置(config.media_position):left 图/视频在左(默认)/ right 在右;移动端始终文案在上
|
|
|
|
|
|
const mediaPosition = computed(() => (props.section.config as Record<string, string> | null)?.media_position === 'right' ? 'right' : 'left');
|
|
|
|
|
|
|
2026-08-10 14:56:44 +08:00
|
|
|
|
// 分栏比例(config.split_ratio):媒体栏宽占比,档位 30/40/50/60/70,默认 50(五五等分);移动端单列不受影响
|
|
|
|
|
|
const splitRatio = computed(() => {
|
|
|
|
|
|
const v = Number((props.section.config as Record<string, unknown> | null)?.split_ratio);
|
|
|
|
|
|
return [30, 40, 50, 60, 70].includes(v) ? v : 50;
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
|
|
// 栅格轨道随媒体位置:媒体在左 = 首轨为媒体栏,媒体在右 = 次轨为媒体栏(完整字面量供 UnoCSS 提取)
|
|
|
|
|
|
const gridClass = computed(() =>
|
|
|
|
|
|
mediaPosition.value === 'right'
|
|
|
|
|
|
? 'md:grid-cols-[minmax(0,1fr)_var(--split-media,50%)]'
|
|
|
|
|
|
: 'md:grid-cols-[var(--split-media,50%)_minmax(0,1fr)]'
|
|
|
|
|
|
);
|
|
|
|
|
|
|
2026-08-05 18:36:01 +08:00
|
|
|
|
// 媒体填满所在栏(去掉 520px 上限与栏内居中),高度按原素材比例自适应
|
2026-08-04 19:18:35 +08:00
|
|
|
|
const mediaWrapClass = computed(() =>
|
|
|
|
|
|
mediaPosition.value === 'right'
|
2026-08-05 18:36:01 +08:00
|
|
|
|
? 'order-2'
|
|
|
|
|
|
: 'order-2 md:order-1'
|
2026-08-04 19:18:35 +08:00
|
|
|
|
);
|
|
|
|
|
|
|
|
|
|
|
|
const textWrapClass = computed(() =>
|
|
|
|
|
|
mediaPosition.value === 'right'
|
|
|
|
|
|
? 'flex flex-col gap-20px order-1'
|
|
|
|
|
|
: 'flex flex-col gap-20px order-1 md:order-2'
|
|
|
|
|
|
);
|
2026-07-31 19:02:00 +08:00
|
|
|
|
</script>
|
|
|
|
|
|
|
|
|
|
|
|
<template>
|
2026-08-10 14:56:44 +08:00
|
|
|
|
<div
|
|
|
|
|
|
class="section-container py-[var(--sec-py,96px)] grid gap-48px items-center"
|
|
|
|
|
|
:class="gridClass"
|
|
|
|
|
|
:style="{ '--split-media': `${splitRatio}%` }"
|
|
|
|
|
|
>
|
2026-08-04 19:18:35 +08:00
|
|
|
|
<!-- 媒体:视频优先,其次图片 -->
|
|
|
|
|
|
<div v-if="videoUrl || mediaUrl" :class="mediaWrapClass">
|
|
|
|
|
|
<video
|
|
|
|
|
|
v-if="videoUrl"
|
|
|
|
|
|
:src="videoUrl"
|
2026-08-05 18:36:01 +08:00
|
|
|
|
class="w-full rounded-16px object-cover shadow-lg"
|
2026-08-04 19:18:35 +08:00
|
|
|
|
autoplay
|
|
|
|
|
|
muted
|
|
|
|
|
|
loop
|
|
|
|
|
|
playsinline
|
|
|
|
|
|
/>
|
2026-08-05 18:36:01 +08:00
|
|
|
|
<img v-else :src="mediaUrl" :alt="title" class="w-full rounded-16px object-cover shadow-lg" />
|
2026-07-31 19:02:00 +08:00
|
|
|
|
</div>
|
|
|
|
|
|
|
|
|
|
|
|
<!-- 文案 -->
|
2026-08-04 19:18:35 +08:00
|
|
|
|
<div :class="textWrapClass">
|
|
|
|
|
|
<p v-if="overline" class="text-14px font-semibold tracking-widest uppercase" :class="isDark ? 'text-primary-300' : 'text-primary'" :style="overlineStyle">
|
2026-07-31 19:02:00 +08:00
|
|
|
|
{{ overline }}
|
|
|
|
|
|
</p>
|
2026-08-05 18:36:01 +08:00
|
|
|
|
<h2 class="font-bold leading-tight" :class="isDark ? 'text-white' : 'text-gray-900 dark:text-white'" :style="titleStyle">
|
2026-07-31 19:02:00 +08:00
|
|
|
|
{{ title }}
|
|
|
|
|
|
</h2>
|
2026-08-03 19:09:03 +08:00
|
|
|
|
<p v-if="subtitle" class="text-lg" :class="isDark ? 'text-gray-300' : 'text-gray-600 dark:text-gray-300'" :style="bodyStyle">
|
2026-07-31 19:02:00 +08:00
|
|
|
|
{{ subtitle }}
|
|
|
|
|
|
</p>
|
2026-08-05 18:36:01 +08:00
|
|
|
|
<div v-if="content" class="rich-text text-15px leading-relaxed" :class="isDark ? 'text-gray-400' : 'text-gray-500 dark:text-gray-400'" :style="bodyStyle" v-html="content" />
|
2026-07-31 19:02:00 +08:00
|
|
|
|
<SectionCta :section="section" class="mt-8px" />
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</div>
|
|
|
|
|
|
</template>
|