<script setup lang="ts">
|
import type { MenuSwiperItemProperty, MenuSwiperProperty } from './config';
|
|
import { ref, watch } from 'vue';
|
|
import { Carousel, Image } from 'ant-design-vue';
|
|
/** 菜单导航 */
|
defineOptions({ name: 'MenuSwiper' });
|
|
const props = defineProps<{ property: MenuSwiperProperty }>();
|
|
const TITLE_HEIGHT = 20; // 标题的高度
|
const ICON_SIZE = 32; // 图标的高度
|
const SPACE_Y = 16; // 垂直间距:一行上下的间距
|
|
const pages = ref<MenuSwiperItemProperty[][]>([]); // 分页
|
const carouselHeight = ref(0); // 轮播图高度
|
|
const rowHeight = ref(0); // 行高
|
const columnWidth = ref(''); // 列宽
|
|
watch(
|
() => props.property,
|
() => {
|
// 计算列宽:每一列的百分比
|
columnWidth.value = `${100 * (1 / props.property.column)}%`;
|
// 计算行高:图标 + 文字(仅显示图片时为0) + 垂直间距 * 2
|
rowHeight.value =
|
(props.property.layout === 'iconText'
|
? ICON_SIZE + TITLE_HEIGHT
|
: ICON_SIZE) +
|
SPACE_Y * 2;
|
// 计算轮播的高度:行数 * 行高
|
carouselHeight.value = props.property.row * rowHeight.value;
|
|
// 每页数量:行数 * 列数
|
const pageSize = props.property.row * props.property.column;
|
// 清空分页
|
pages.value = [];
|
// 每一页的菜单
|
let pageItems: MenuSwiperItemProperty[] = [];
|
for (const item of props.property.list) {
|
// 本页满员,新建下一页
|
if (pageItems.length === pageSize) {
|
pageItems = [];
|
}
|
// 增加一页
|
if (pageItems.length === 0) {
|
pages.value.push(pageItems);
|
}
|
// 本页增加一个
|
pageItems.push(item);
|
}
|
},
|
{ immediate: true, deep: true },
|
);
|
</script>
|
|
<template>
|
<Carousel
|
:autoplay="false"
|
arrows
|
dots
|
:style="{ height: `${carouselHeight}px` }"
|
>
|
<div v-for="(page, pageIndex) in pages" :key="pageIndex">
|
<div class="flex flex-row flex-wrap">
|
<div
|
v-for="(item, index) in page"
|
:key="index"
|
class="relative flex flex-col items-center justify-center"
|
:style="{ width: columnWidth, height: `${rowHeight}px` }"
|
>
|
<div class="relative" :class="`h-${ICON_SIZE}px w-${ICON_SIZE}px`">
|
<span
|
v-if="item.badge?.show"
|
class="absolute -right-2.5 -top-2.5 z-10 h-5 rounded-[10px] px-1.5 text-center text-xs leading-5"
|
:style="{
|
color: item.badge.textColor,
|
backgroundColor: item.badge.bgColor,
|
}"
|
>
|
{{ item.badge.text }}
|
</span>
|
<Image
|
v-if="item.iconUrl"
|
:src="item.iconUrl"
|
class="h-full w-full"
|
:preview="false"
|
/>
|
</div>
|
<span
|
v-if="property.layout === 'iconText'"
|
class="text-xs"
|
:style="{
|
color: item.titleColor,
|
height: `${TITLE_HEIGHT}px`,
|
lineHeight: `${TITLE_HEIGHT}px`,
|
}"
|
>
|
{{ item.title }}
|
</span>
|
</div>
|
</div>
|
</div>
|
</Carousel>
|
</template>
|
<style lang="scss">
|
// Ant Design Vue Carousel 样式调整
|
:deep(.ant-carousel .ant-carousel-dots) {
|
.ant-carousel-dot {
|
width: 6px;
|
height: 6px;
|
border-radius: 6px;
|
|
button {
|
width: 6px;
|
height: 6px;
|
background: hsl(var(--red));
|
border-radius: 6px;
|
}
|
}
|
|
.ant-carousel-dot-active button {
|
width: 12px;
|
background: hsl(var(--red));
|
}
|
}
|
</style>
|