feat: 首页板块的轮播实现
This commit is contained in:
1 parent
f23d598328
commit
d4790ba82c
4 files changed
+73
-33
No files matched your search
@@ -11,16 +11,36 @@
|
||||
* → 因此**高度由组件自持**(sizeCls,按 variant 给),不依赖父级;
|
||||
* **宽度**仍由父级在宿主节点上分配(flex-[1.06] / flex-1)。
|
||||
*/
|
||||
import { computed } from "vue";
|
||||
import { computed, ref } from "vue";
|
||||
import { goCatalog } from "@/utils/nav";
|
||||
|
||||
const props = defineProps({
|
||||
section: { type: Object, required: true },
|
||||
variant: { type: String, default: "short" },
|
||||
/**
|
||||
* 卡内轮播图集。首页旅居卡传「每个分组一张代表图」(4 张),
|
||||
* 传空则回落为板块封面单图(右二半高卡就是这种)。
|
||||
*/
|
||||
covers: { type: Array, default: () => [] },
|
||||
});
|
||||
|
||||
const isTall = computed(() => props.variant === "tall");
|
||||
|
||||
/** 轮播图集:有 covers 用 covers,否则单张封面 —— 统一成一个数组,模板只判长度 */
|
||||
const images = computed(() => {
|
||||
if (props.covers?.length) return props.covers;
|
||||
return props.section?.coverImage ? [props.section.coverImage] : [];
|
||||
});
|
||||
/** ≥2 张才起轮播:单张时 swiper 没有意义,还会白白多一层节点 */
|
||||
const hasSlider = computed(() => images.value.length > 1);
|
||||
|
||||
const activeIndex = ref(0);
|
||||
/** 取模防越界:图集长度变化(如热更新/接口返回变少)时进度点不会全部熄灭 */
|
||||
const activeDot = computed(() => (images.value.length ? activeIndex.value % images.value.length : 0));
|
||||
const onSwiperChange = (e) => {
|
||||
activeIndex.value = Number(e.detail?.current || 0);
|
||||
};
|
||||
|
||||
/**
|
||||
* ⚠️ 卡片高度**由组件自持**,不吃父级传来的尺寸。
|
||||
* 原因:小程序自定义组件是「宿主节点 → 内部根节点」两层,父级写在
|
||||
@@ -66,7 +86,24 @@ const onTap = () => goCatalog(props.section.key);
|
||||
hover-class="opacity-95"
|
||||
@click="onTap"
|
||||
>
|
||||
<image class="absolute inset-0 w-full h-full" :src="section.coverImage" mode="aspectFill" />
|
||||
<!-- 图:≥2 张自动轮播(首页旅居卡 = 酒店/民宿/公寓/露营 四个分组各一张),否则单图。
|
||||
swiper 用 absolute inset-0 填满卡片 —— 根节点高度由 sizeCls 自持(226px),
|
||||
百分比高度链路在小程序里太脆,靠绝对定位拿确定高度最稳。 -->
|
||||
<swiper
|
||||
v-if="hasSlider"
|
||||
class="absolute inset-0 w-full h-full"
|
||||
circular
|
||||
autoplay
|
||||
:interval="3500"
|
||||
:duration="500"
|
||||
:indicator-dots="false"
|
||||
@change="onSwiperChange"
|
||||
>
|
||||
<swiper-item v-for="url in images" :key="url">
|
||||
<image class="w-full h-full" :src="url" mode="aspectFill" />
|
||||
</swiper-item>
|
||||
</swiper>
|
||||
<image v-else class="absolute inset-0 w-full h-full" :src="section.coverImage" mode="aspectFill" />
|
||||
|
||||
<!-- 遮罩:文字锚顶部 → 顶部最重 -->
|
||||
<view class="absolute inset-0" :class="isTall ? 'scrim-tile-tall' : 'scrim-tile'" />
|
||||
@@ -86,11 +123,14 @@ const onTap = () => goCatalog(props.section.key);
|
||||
<AppIcon name="arrow" tone="white" :size="isTall ? 22 : 17" />
|
||||
</view>
|
||||
|
||||
<!-- 通高卡右下角放轮播指示点 -->
|
||||
<view v-if="isTall" class="absolute left-[15px] bottom-[23px] flex gap-[5px] z-[2]">
|
||||
<view class="w-[14px] h-[5px] rounded-full bg-white" />
|
||||
<view class="w-[5px] h-[5px] rounded-full bg-[rgba(255,255,255,0.45)]" />
|
||||
<view class="w-[5px] h-[5px] rounded-full bg-[rgba(255,255,255,0.45)]" />
|
||||
<!-- 通高卡左下角轮播指示点:张数 = 图集张数(旅居卡 4 个分组 → 4 个点) -->
|
||||
<view v-if="isTall && hasSlider" class="absolute left-[15px] bottom-[23px] flex gap-[5px] z-[2]">
|
||||
<view
|
||||
v-for="(url, i) in images"
|
||||
:key="`${url}-dot`"
|
||||
class="h-[5px] rounded-full transition-all duration-300"
|
||||
:class="i === activeDot ? 'w-[14px] bg-white' : 'w-[5px] bg-[rgba(255,255,255,0.45)]'"
|
||||
/>
|
||||
</view>
|
||||
</view>
|
||||
</template>
|
||||
Reference in new issue
Block a user