You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

364 lines
8.6 KiB
Vue

This file contains ambiguous Unicode characters!

This file contains ambiguous Unicode characters that may be confused with others in your current locale. If your use case is intentional and legitimate, you can safely ignore this warning. Use the Escape button to highlight these characters.

<!-- 装修基础组件菜单导航金刚区 -->
<template>
<!-- 包裹层 -->
<view
class="ui-swiper"
:class="[props.mode, props.bg, props.ui]"
:style="[{ height: swiperHeight + (menuList.length > 1 ? 50 : 0) + 'rpx' }]"
>
<!-- 轮播 -->
<swiper
:circular="props.circular"
:current="state.cur"
:autoplay="props.autoplay"
:interval="props.interval"
:duration="props.duration"
:style="[{ height: swiperHeight + 'rpx' }]"
@change="swiperChange"
>
<swiper-item
v-for="(arr, index) in menuList"
:key="index"
:class="{ cur: state.cur == index }"
>
<!-- 宫格 -->
<view class="grid-wrap">
<view
v-for="(item, index) in arr"
:key="index"
class="grid-item ss-flex ss-flex-col ss-col-center ss-row-center"
:style="[{ width: `${100 * (1 / data.column)}%`, height: '200rpx' }]"
hover-class="ss-hover-btn"
@tap="sheep.$router.go(item.url)"
>
<view class="menu-box ss-flex ss-flex-col ss-col-center ss-row-center">
<view
v-if="item.badge.show"
class="tag-box"
:style="[{ background: item.badge.bgColor, color: item.badge.textColor }]"
>
{{ item.badge.text }}
</view>
<image
v-if="item.iconUrl"
class="menu-icon"
:style="[
{
width: props.iconSize + 'rpx',
height: props.iconSize + 'rpx',
},
]"
:src="sheep.$url.cdn(item.iconUrl)"
mode="aspectFill"
></image>
<view
v-if="data.layout === 'iconText'"
class="menu-title"
:style="[{ color: item.titleColor }]"
>
{{ item.title }}
</view>
</view>
</view>
</view>
</swiper-item>
</swiper>
<!-- 指示点 -->
<template v-if="menuList.length > 1">
<view class="ui-swiper-dot" :class="props.dotStyle" v-if="props.dotStyle != 'tag'">
<view
class="line-box"
v-for="(item, index) in menuList.length"
:key="index"
:class="[state.cur == index ? 'cur' : '', props.dotCur]"
></view>
</view>
<view class="ui-swiper-dot" :class="props.dotStyle" v-if="props.dotStyle == 'tag'">
<view class="ui-tag radius" :class="[props.dotCur]" style="pointer-events: none">
<view style="transform: scale(0.7)">{{ state.cur + 1 }} / {{ menuList.length }}</view>
</view>
</view>
</template>
</view>
</template>
<script setup>
/**
* 轮播menu
*
* @property {Boolean} circular = false - 是否采用衔接滑动,即播放到末尾后重新回到开头
* @property {Boolean} autoplay = true - 是否自动切换
* @property {Number} interval = 5000 - 自动切换时间间隔
* @property {Number} duration = 500 - 滑动动画时长,app-nvue不支持
* @property {Array} list = [] - 轮播数据
* @property {String} ui = '' - 样式class
* @property {String} mode - 模式
* @property {String} dotStyle - 指示点样式
* @property {String} dotCur= 'ui-BG-Main' - 当前指示点样式,默认主题色
* @property {String} bg - 背景
*
* @property {String|Number} col = 4 - 一行数量
* @property {String|Number} row = 1 - 几行
* @property {String} hasBorder - 是否有边框
* @property {String} borderColor - 边框颜色
* @property {String} background - 背景
* @property {String} hoverClass - 按压样式类
* @property {String} hoverStayTime - 动画时间
*
* @property {Array} list - 导航列表
* @property {Number} iconSize - 图标大小
* @property {String} color - 标题颜色
*
*/
import { reactive, computed } from 'vue';
import sheep from '@/sheep';
// 数据
const state = reactive({
cur: 0,
});
// 接收参数
const props = defineProps({
data: {
type: Object,
default() {},
},
styles: {
type: Object,
default() {},
},
circular: {
type: Boolean,
default: true,
},
autoplay: {
type: Boolean,
default: false,
},
interval: {
type: Number,
default: 5000,
},
duration: {
type: Number,
default: 500,
},
ui: {
type: String,
default: '',
},
mode: {
//default
type: String,
default: 'default',
},
dotStyle: {
type: String,
default: 'long', //default long tag
},
dotCur: {
type: String,
default: 'ui-BG-Main',
},
bg: {
type: String,
default: 'bg-none',
},
height: {
type: Number,
default: 300,
},
// 是否有边框
hasBorder: {
type: Boolean,
default: true,
},
// 边框颜色
borderColor: {
type: String,
default: 'red',
},
background: {
type: String,
default: 'blue',
},
hoverClass: {
type: String,
default: 'ss-hover-class', //'none'为没有hover效果
},
// 一排宫格数
col: {
type: [Number, String],
default: 3,
},
iconSize: {
type: Number,
default: 80,
},
color: {
type: String,
default: '#000',
},
});
// 生成数据
const menuList = computed(() => splitData(props.data.list, props.data.row * props.data.column));
const swiperHeight = computed(() => props.data.row * (props.data.layout === 'iconText' ? 200 : 180));
const windowWidth = sheep.$platform.device.windowWidth;
// current 改变时会触发 change 事件
const swiperChange = (e) => {
state.cur = e.detail.current;
};
// 重组数据
const splitData = (oArr = [], length = 1) => {
let arr = [];
let minArr = [];
oArr.forEach((c) => {
if (minArr.length === length) {
minArr = [];
}
if (minArr.length === 0) {
arr.push(minArr);
}
minArr.push(c);
});
return arr;
};
</script>
<style lang="scss" scoped>
.grid-wrap {
width: 100%;
display: flex;
position: relative;
box-sizing: border-box;
overflow: hidden;
flex-wrap: wrap;
align-items: center;
}
.menu-box {
position: relative;
z-index: 1;
transform: translate(0, 0);
.tag-box {
position: absolute;
z-index: 2;
top: 0;
right: -6rpx;
font-size: 2em;
line-height: 1;
padding: 0.4em 0.6em 0.3em;
transform: scale(0.4) translateX(0.5em) translatey(-0.6em);
transform-origin: 100% 0;
border-radius: 200rpx;
white-space: nowrap;
}
.menu-icon {
transform: translate(0, 0);
width: 80rpx;
height: 80rpx;
padding-bottom: 10rpx;
}
.menu-title {
font-size: 24rpx;
color: #333;
}
}
::v-deep(.ui-swiper) {
position: relative;
z-index: 1;
.ui-swiper-dot {
position: absolute;
width: 100%;
bottom: 20rpx;
height: 30rpx;
display: flex;
align-items: center;
justify-content: center;
z-index: 2;
&.default .line-box {
display: inline-flex;
border-radius: 50rpx;
width: 6px;
height: 6px;
border: 2px solid transparent;
margin: 0 10rpx;
opacity: 0.3;
position: relative;
justify-content: center;
align-items: center;
&.cur {
width: 8px;
height: 8px;
opacity: 1;
border: 0px solid transparent;
}
&.cur::after {
content: '';
border-radius: 50rpx;
width: 4px;
height: 4px;
background-color: #fff;
}
}
&.long .line-box {
display: inline-block;
border-radius: 100rpx;
width: 6px;
height: 6px;
margin: 0 10rpx;
opacity: 0.3;
position: relative;
&.cur {
width: 24rpx;
opacity: 1;
}
&.cur::after {
}
}
&.line {
bottom: 20rpx;
.line-box {
display: inline-block;
width: 30px;
height: 3px;
opacity: 0.3;
position: relative;
&.cur {
opacity: 1;
}
}
}
&.tag {
justify-content: flex-end;
position: absolute;
bottom: 20rpx;
right: 20rpx;
}
}
}
</style>