209 lines
4.3 KiB
Vue
209 lines
4.3 KiB
Vue
![]() |
<template>
|
|||
|
<fix-base-style
|
|||
|
:styleSpacing="styleSpacing"
|
|||
|
:styleColor="styleColor"
|
|||
|
:position="position"
|
|||
|
:index="index"
|
|||
|
>
|
|||
|
<view class="fix-menus">
|
|||
|
<swiper
|
|||
|
class="inner"
|
|||
|
:style="{
|
|||
|
height: height,
|
|||
|
}"
|
|||
|
:circular="false"
|
|||
|
:indicator-dots="false"
|
|||
|
@change="changeCurrent"
|
|||
|
>
|
|||
|
<swiper-item v-for="(item, index) in tabs" :key="index">
|
|||
|
<view
|
|||
|
class="row"
|
|||
|
:class="[`is-${style.rowNum}`]"
|
|||
|
v-for="(row, r) in item"
|
|||
|
:key="r"
|
|||
|
>
|
|||
|
<view
|
|||
|
class="item"
|
|||
|
v-for="(col, c) in row"
|
|||
|
:key="c"
|
|||
|
@click="toPath(col.link)"
|
|||
|
>
|
|||
|
<view
|
|||
|
:class="[`is-${style.shape}`, `is-${col.mode}`]"
|
|||
|
:style="{ backgroundColor: col.backgroundColor }"
|
|||
|
class="icon-box"
|
|||
|
>
|
|||
|
<cl-image :src="col.icon" mode="aspectFill" class="icon">
|
|||
|
</cl-image>
|
|||
|
</view>
|
|||
|
<span
|
|||
|
v-if="col.mode == 'mode-1'"
|
|||
|
class="text"
|
|||
|
:style="{ color: col.color }"
|
|||
|
>{{ col.text }}</span
|
|||
|
>
|
|||
|
</view>
|
|||
|
</view>
|
|||
|
</swiper-item>
|
|||
|
</swiper>
|
|||
|
<!-- 自定义指示器 -->
|
|||
|
<view class="custom-indicator">
|
|||
|
<view
|
|||
|
v-for="(item, index) in tabs"
|
|||
|
:key="index"
|
|||
|
class="custom-indicator-dot"
|
|||
|
:class="{ active: current === index }"
|
|||
|
></view>
|
|||
|
</view>
|
|||
|
</view>
|
|||
|
</fix-base-style>
|
|||
|
</template>
|
|||
|
|
|||
|
<script lang="ts" setup name="fix-menus">
|
|||
|
import { computed, ref, type PropType } from "vue";
|
|||
|
import type { Form } from "../../types/form";
|
|||
|
import { baseProps } from "../../hooks";
|
|||
|
|
|||
|
const emits = defineEmits(["jump"]);
|
|||
|
const props = defineProps({
|
|||
|
style: {
|
|||
|
type: Object,
|
|||
|
default: () => {
|
|||
|
return {
|
|||
|
shape: "round",
|
|||
|
pageNum: 1,
|
|||
|
rowNum: 3,
|
|||
|
};
|
|||
|
},
|
|||
|
},
|
|||
|
list: {
|
|||
|
type: Array as PropType<Form.Menu[]>,
|
|||
|
default: () => [],
|
|||
|
},
|
|||
|
index: {
|
|||
|
type: Number,
|
|||
|
default: 0,
|
|||
|
},
|
|||
|
...baseProps,
|
|||
|
});
|
|||
|
const current = ref(0);
|
|||
|
function changeCurrent(event: any) {
|
|||
|
current.value = event.detail.current;
|
|||
|
}
|
|||
|
const height = computed(() => {
|
|||
|
return 180 * props.style.pageNum + "rpx";
|
|||
|
});
|
|||
|
const tabs = computed(() => {
|
|||
|
return paginateArray(props.list, props.style.pageNum, props.style.rowNum);
|
|||
|
});
|
|||
|
|
|||
|
// 解决 每页1行 每行3个 的问题
|
|||
|
function paginateArray(data: Form.Menu[], pageNum: number, rowNum: number) {
|
|||
|
const result: any[] = [];
|
|||
|
const totalItems = data.length;
|
|||
|
let pageIndex = 0;
|
|||
|
|
|||
|
while (pageIndex < totalItems) {
|
|||
|
const page: any[] = [];
|
|||
|
for (let i = 0; i < pageNum && pageIndex < totalItems; i++) {
|
|||
|
const row: any[] = [];
|
|||
|
for (let j = 0; j < rowNum && pageIndex < totalItems; j++) {
|
|||
|
row.push(data[pageIndex]);
|
|||
|
pageIndex++;
|
|||
|
}
|
|||
|
page.push(row);
|
|||
|
}
|
|||
|
result.push(page);
|
|||
|
}
|
|||
|
|
|||
|
return result;
|
|||
|
}
|
|||
|
|
|||
|
function toPath(link: Form.Link) {
|
|||
|
emits("jump", link);
|
|||
|
}
|
|||
|
</script>
|
|||
|
|
|||
|
<style lang="scss" scoped>
|
|||
|
.fix-menus {
|
|||
|
width: 100%;
|
|||
|
padding-bottom: 20rpx;
|
|||
|
.inner {
|
|||
|
height: 100%;
|
|||
|
padding: 10rpx;
|
|||
|
box-sizing: border-box;
|
|||
|
.row {
|
|||
|
margin-bottom: 20rpx;
|
|||
|
.item {
|
|||
|
display: flex;
|
|||
|
flex-direction: column;
|
|||
|
justify-content: center;
|
|||
|
align-items: center;
|
|||
|
height: 160rpx;
|
|||
|
box-sizing: border-box;
|
|||
|
|
|||
|
.is-round {
|
|||
|
border-radius: 40rpx;
|
|||
|
}
|
|||
|
.is-mode-2 {
|
|||
|
width: 120rpx !important;
|
|||
|
height: 120rpx !important;
|
|||
|
}
|
|||
|
.icon-box {
|
|||
|
width: 80rpx;
|
|||
|
height: 80rpx;
|
|||
|
overflow: hidden;
|
|||
|
.icon {
|
|||
|
width: 100%;
|
|||
|
height: 100%;
|
|||
|
}
|
|||
|
}
|
|||
|
.text {
|
|||
|
font-size: 26rpx;
|
|||
|
margin-top: 12rpx;
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
.row:last-child {
|
|||
|
margin-bottom: 0;
|
|||
|
}
|
|||
|
.is-3 {
|
|||
|
display: grid;
|
|||
|
grid-template-columns: repeat(3, 1fr); /* 每行3列,每列宽度相等 */
|
|||
|
gap: 20rpx; /* 间距 */
|
|||
|
}
|
|||
|
.is-4 {
|
|||
|
display: grid;
|
|||
|
grid-template-columns: repeat(4, 1fr); /* 每行3列,每列宽度相等 */
|
|||
|
gap: 20rpx; /* 间距 */
|
|||
|
}
|
|||
|
.is-5 {
|
|||
|
display: grid;
|
|||
|
grid-template-columns: repeat(5, 1fr); /* 每行3列,每列宽度相等 */
|
|||
|
gap: 20rpx; /* 间距 */
|
|||
|
}
|
|||
|
}
|
|||
|
}
|
|||
|
.custom-indicator {
|
|||
|
display: flex;
|
|||
|
justify-content: center;
|
|||
|
}
|
|||
|
|
|||
|
.custom-indicator-dot {
|
|||
|
width: 12rpx;
|
|||
|
height: 12rpx;
|
|||
|
background-color: #686580;
|
|||
|
border-radius: 50%; /* 默认圆形 */
|
|||
|
margin: 0 10rpx;
|
|||
|
transition: all 0.3s ease; /* 过渡效果 */
|
|||
|
}
|
|||
|
|
|||
|
.custom-indicator-dot.active {
|
|||
|
width: 40rpx; /* 激活状态变成长方形 */
|
|||
|
height: 12rpx;
|
|||
|
background: var(--btn-color) !important;
|
|||
|
border-radius: 20rpx; /* 调整边角的圆度 */
|
|||
|
transition: all 0.5s ease; /* 激活状态有更慢的动画过渡 */
|
|||
|
}
|
|||
|
</style>
|