Files
admin/www/app/components/section/layouts/LayoutSplitContent.vue
T
2026-08-13 16:48:05 +08:00

78 lines
3.2 KiB
Vue
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
<script setup lang="ts">
import type { SectionBlock } from '~/types/site';
import { resolveAssetUrl } from '~/utils/resolveAssetUrl';
const props = defineProps<{ section: SectionBlock }>();
const { overline, title, subtitle, content, mediaUrl, isDark, overlineStyle, titleStyle, bodyStyle } = useSectionContent(props.section);
// 分栏媒体视频(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');
// 分栏比例(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)]'
);
// 媒体填满所在栏(去掉 520px 上限与栏内居中),高度按原素材比例自适应
const mediaWrapClass = computed(() =>
mediaPosition.value === 'right'
? 'order-2'
: 'order-2 md:order-1'
);
const textWrapClass = computed(() =>
mediaPosition.value === 'right'
? 'flex flex-col gap-20px order-1'
: 'flex flex-col gap-20px order-1 md:order-2'
);
</script>
<template>
<div
class="section-container py-[var(--sec-py,96px)] grid gap-48px items-center"
:class="gridClass"
:style="{ '--split-media': `${splitRatio}%` }"
>
<!-- 媒体视频优先其次图片 -->
<div v-if="videoUrl || mediaUrl" :class="mediaWrapClass">
<video
v-if="videoUrl"
:src="videoUrl"
class="w-full rounded-16px object-cover shadow-lg"
autoplay
muted
loop
playsinline
/>
<img v-else :src="mediaUrl" :alt="title" class="w-full rounded-16px object-cover shadow-lg" />
</div>
<!-- 文案 -->
<div :class="textWrapClass">
<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="font-bold leading-tight" :class="isDark ? 'text-white' : 'text-gray-900 dark:text-white'" :style="titleStyle">
{{ title }}
</h2>
<p v-if="subtitle" class="text-lg" :class="isDark ? 'text-gray-300' : 'text-gray-600 dark:text-gray-300'" :style="bodyStyle">
{{ subtitle }}
</p>
<div v-if="content" class="rich-text text-16px leading-relaxed" :class="isDark ? 'text-gray-400' : 'text-gray-500 dark:text-gray-400'" :style="bodyStyle" v-html="content" />
<SectionCta :section="section" class="mt-8px" />
</div>
</div>
</template>