组件代码
<template>
<div :class="['SwiperBoard', swiperUuid]">
<article v-if="dataList.length" class="list_box" v-resize="onResize">
<div class="hedaer_box" :style="{ width: boxWidth + 'px' }">
<span class="title_box">{{ title }}</span>
<span v-if="totalPages > 1" class="right_box">
<icon-left @click="prev" :style="page === 1 ? 'color: #EFF0F1;' : ''" />
{{ page }}/{{ totalPages }}
<icon-right @click="next" :style="page === totalPages ? 'color: #EFF0F1;' : ''" />
</span>
</div>
<div class="swiper_box" :style="{ width: boxWidth + 'px' }">
<template v-for="(item, index) in dataList">
<div :class="['swiper_item', 'swiper_item' + index]" :style="`padding:${cardPadding};`">
<slot :record="item" :index="index"></slot>
</div>
</template>
</div>
</article>
</div>
</template>
<script setup>
import { ref, computed, watch, nextTick } from "vue";
// 组件实例的唯一标识
const swiperUuid = "id" + uuid();
/**
* 注意 【 间距采用 卡片 cardPadding 调整 】
* */
const props = defineProps({
dataList: {
type: Array,
default: [],
},
title: {
type: String,
},
cardPadding: {
type: String,
default: "10px",
},
});
watch(
() => props.dataList,
(val) => {
if (val.length) {
nextTick(() => {
itemWidth.value = document.querySelector(`.${swiperUuid} .swiper_item`).clientWidth;
});
}
},
{ deep: true, immediate: true }
);
const curIndex = ref(0);
const itemWidth = ref(0);
const showItemLength = ref(5);
const boxWidth = computed(() => {
return itemWidth.value * showItemLength.value;
});
const page = computed(() => {
return Math.ceil(curIndex.value / showItemLength.value) + 1;
});
const totalPages = computed(() => {
return Math.ceil(props.dataList.length / showItemLength.value);
});
const next = () => {
let index =
curIndex.value + showItemLength.value > props.dataList.length - 1 - showItemLength.value
? props.dataList.length - 1 - showItemLength.value
: curIndex.value + showItemLength.value;
const curSwiperItem = document.querySelector(`.${swiperUuid} .swiper_item${index}`);
if (curSwiperItem) {
curIndex.value = index;
curSwiperItem.scrollIntoView({
behavior: "smooth",
block: "nearest",
inline: "start",
});
}
};
const prev = () => {
let index = curIndex.value - showItemLength.value < 0 ? 0 : curIndex.value - showItemLength.value;
const curSwiperItem = document.querySelector(`.${swiperUuid} .swiper_item${index}`);
if (curSwiperItem) {
curIndex.value = index;
curSwiperItem.scrollIntoView({
behavior: "smooth",
block: "nearest",
inline: "start",
});
}
};
const onResize = (el) => {
showItemLength.value = Math.floor(el.clientWidth / itemWidth.value);
};
function uuid() {
return "xxxxxxxx".replace(/[xy]/g, function (c) {
var r = (Math.random() * 16) | 0,
v = c == "x" ? r : (r & 0x3) | 0x8;
return v.toString(16);
});
}
</script>
<style lang="scss" scoped>
.SwiperBoard {
margin: 15px 0;
.hedaer_box {
margin: 0 auto;
padding: 0 10px;
box-sizing: border-box;
display: flex;
justify-content: space-between;
align-items: center;
.title_box {
font-size: 28px;
font-weight: 600;
color: #1c1c1d;
}
.right_box {
display: flex;
align-items: center;
color: #7f7f82;
user-select: none;
}
}
.swiper_box {
display: flex;
white-space: nowrap;
overflow-x: auto;
margin: 0 auto;
&::-webkit-scrollbar {
height: 0;
}
.swiper_item {
display: inline-block;
}
:last-child {
margin-right: 0;
}
}
}
</style>
配合 v-resize 自定义组件完成自适应
export const useDirective = (app) => {
// 注册自定义指令 v-resize
app.directive("resize", {
bind(el, binding) {
// el为绑定的元素,binding 为绑定给指令的对象
let width = "";
let height = "";
function isReize() {
const style = document.defaultView.getComputedStyle(el);
if (width !== style.width || height !== style.height) {
binding.value(el);
}
width = style.width;
height = style.height;
}
el.__vueSetInterval__ = setInterval(isReize, 300);
},
unbind(el) {
clearInterval(el.__vueSetInterval__);
},
});
};
导入到 main.js 里挂载
使用
<SwiperBoard :dataList="dataList" :title="scoped.name" cardPadding="16px 10px">
<template #default="{ record, index }">
<div class="card_wrap">
// 自定义卡片
</div>
</template>
</SwiperBoard>